PHPackages                             tobento/service-migration - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. tobento/service-migration

ActiveLibrary[Database &amp; ORM](/categories/database)

tobento/service-migration
=========================

A mirgation manager for any PHP application.

2.0(7mo ago)03346MITPHPPHP &gt;=8.4

Since Oct 20Pushed 7mo ago1 watchersCompare

[ Source](https://github.com/tobento-ch/service-migration)[ Packagist](https://packagist.org/packages/tobento/service-migration)[ Docs](https://www.tobento.ch)[ RSS](/packages/tobento-service-migration/feed)WikiDiscussions 2.x Synced 1mo ago

READMEChangelog (10)Dependencies (7)Versions (15)Used By (6)

Migration Service
=================

[](#migration-service)

The Migration Service provides a flexible way for handling migrations for PHP applications.

Table of Contents
-----------------

[](#table-of-contents)

- [Getting started](#getting-started)
    - [Requirements](#requirements)
    - [Highlights](#highlights)
    - [Simple Example](#simple-example)
- [Documentation](#documentation)
    - [Migration](#migration)
        - [Create Migration](#create-migration)
        - [Actions](#actions)
            - [Callable](#callable)
            - [Dir Copy](#dir-copy)
            - [Dir Delete](#dir-delete)
            - [Fail](#fail)
            - [Files Copy](#files-copy)
            - [Files Delete](#files-delete)
            - [File String Replacer](#file-string-replacer)
            - [Null](#null)
            - [PDO Exec](#pdo-exec)
        - [Custom Actions](#custom-actions)
        - [Process Actions](#process-actions)
    - [Migrator](#migrator)
        - [Create Migrator](#create-migrator)
        - [Install Migration](#install-migration)
        - [Uninstall Migration](#uninstall-migration)
        - [Get Installed Migrations](#get-installed-migrations)
        - [Migration Result](#migration-result)
        - [Migration Results](#migration-results)
- [Credits](#credits)

---

Getting started
===============

[](#getting-started)

Add the latest version of the Migration service project running this command.

```
composer require tobento/service-migration

```

Requirements
------------

[](#requirements)

- PHP 8.4 or greater

Highlights
----------

[](#highlights)

- Framework-agnostic, will work with any project
- Decoupled design

Simple Example
--------------

[](#simple-example)

**Create a migration**

```
namespace App\Blog;

use Tobento\Service\Migration\MigrationInterface;
use Tobento\Service\Migration\ActionsInterface;
use Tobento\Service\Migration\Actions;
use Tobento\Service\Migration\Action\FilesCopy;
use Tobento\Service\Migration\Action\FilesDelete;
use Tobento\Service\Migration\Action\DirCopy;
use Tobento\Service\Migration\Action\DirDelete;

/**
 * BlogMigration
 */
class BlogMigration implements MigrationInterface
{
    /**
     * Return a description of the migration.
     *
     * @return string
     */
    public function description(): string
    {
        return 'Blog migration.';
    }

    /**
     * Return the actions to be processed on install.
     *
     * @return ActionsInterface
     */
    public function install(): ActionsInterface
    {
        return new Actions(
            new FilesCopy(
                files: [
                    'dir/to/store/config/' => [
                        'dir/blog/config/blog.php',
                    ],
                ],
                description: 'Blog configuration files installed.',
            ),
            new DirCopy(
                dir: 'dir/blog/views/',
                destDir: 'dir/to/store/views/blog/',
                description: 'Blog view files installed.',
            ),
        );
    }

    /**
     * Return the actions to be processed on uninstall.
     *
     * @return ActionsInterface
     */
    public function uninstall(): ActionsInterface
    {
        return new Actions(
            new FilesDelete(
                files: [
                    'dir/to/store/config/' => [
                        'blog.php',
                    ],
                ],
                description: 'Blog configuration files uninstalled.',
            ),
            new DirDelete(
                dir: 'dir/to/store/views/blog/',
                description: 'Blog view files uninstalled.',
            ),
        );
    }
}
```

**Install / Uninstall migration**

```
use Tobento\Service\Migration\Migrator;
use Tobento\Service\Migration\AutowiringMigrationFactory;
use Tobento\Service\Migration\MigrationJsonFileRepository;
use Tobento\Service\Migration\MigrationInstallException;
use Tobento\Service\Migration\MigrationUninstallException;
use Tobento\Service\Container\Container;

// Any PSR-11 container
$container = new Container();

// Create migrator.
$migrator = new Migrator(
    new AutowiringMigrationFactory($container),
    new MigrationJsonFileRepository(__DIR__.'/migrations/'),
);

// Install a migration.
try {
    $result = $migrator->install(\App\Blog\BlogMigration::class);
} catch (MigrationInstallException $e) {
    // Handle exception.
}

// Uninstall a migration.
if ($migrator->isInstalled(\App\Blog\BlogMigration::class))
{
    try {
        $result = $migrator->uninstall(\App\Blog\BlogMigration::class);
    } catch (MigrationUninstallException $e) {
        // Handle exception.
    }
}
```

Documentation
=============

[](#documentation)

Migration
---------

[](#migration)

### Create Migration

[](#create-migration)

Your migration class must implement the MigrationInterface:

```
use Tobento\Service\Migration\MigrationInterface;
use Tobento\Service\Migration\ActionsInterface;
use Tobento\Service\Migration\Actions;
use Tobento\Service\Migration\Action\FilesCopy;
use Tobento\Service\Migration\Action\FilesDelete;
use Tobento\Service\Migration\Action\DirCopy;
use Tobento\Service\Migration\Action\DirDelete;

/**
 * BlogMigration
 */
class BlogMigration implements MigrationInterface
{
    /**
     * Return a description of the migration.
     *
     * @return string
     */
    public function description(): string
    {
        return 'Blog migration.';
    }

    /**
     * Return the actions to be processed on install.
     *
     * @return ActionsInterface
     */
    public function install(): ActionsInterface
    {
        return new Actions(
            new FilesCopy(
                files: [
                    'dir/to/store/config/' => [
                        'dir/blog/config/blog.php',
                    ],
                ],
                description: 'Blog configuration files installed.',
            ),
            new DirCopy(
                dir: 'dir/blog/views/',
                destDir: 'dir/to/store/views/blog/',
                description: 'Blog view files installed.',
            ),
        );
    }

    /**
     * Return the actions to be processed on uninstall.
     *
     * @return ActionsInterface
     */
    public function uninstall(): ActionsInterface
    {
        return new Actions(
            new FilesDelete(
                files: [
                    'dir/to/store/config/' => [
                        'blog.php',
                    ],
                ],
                description: 'Blog configuration files uninstalled.',
            ),
            new DirDelete(
                dir: 'dir/to/store/views/blog/',
                description: 'Blog view files uninstalled.',
            ),
        );
    }
}
```

### Actions

[](#actions)

### Callable

[](#callable)

The `CallableAction::class` calls the specified callable on process.

```
use Tobento\Service\Migration\Action\CallableAction;
use Tobento\Service\Migration\ActionFailedException;

$action = new CallableAction(
    callable: function ($name) {
        // do something on process
    },
    // you may set parameters passed to the callable:
    parameters: ['name' => 'value'],
    name: 'A unique name', // or null
    description: 'Some description.', // (optional)
    type: 'keyword', // (optional)
);

// Get the callable:
var_dump(is_callable($action->getCallable()));
// bool(true)

// Get the parameters:
var_dump($action->getParameters());
// array(1) { ["name"]=> string(5) "value" }

var_dump($action->description());
// string(17) "Some description."

var_dump($action->type());
// string(7) "keyword"
```

### Dir Copy

[](#dir-copy)

Use the DirCopy::class action to copy a directory to another destination.

```
use Tobento\Service\Migration\Action\DirCopy;

$action = new DirCopy(
    dir: 'dir/blog/views/',
    destDir: 'dir/to/store/views/blog/',
    overwrite: true, // if to overwrite existing dir (default true)
    name: 'A unique name', // or null
    description: 'Blog view files installed.',
    type: 'keyword', // (optional)
);

var_dump($action->getDir());
// string(15) "dir/blog/views/"

var_dump($action->getDestDir());
// string(24) "dir/to/store/views/blog/"

var_dump($action->description());
// string(26) "Blog view files installed."

var_dump($action->type());
// string(7) "keyword"
```

### Dir Delete

[](#dir-delete)

Use the DirDelete::class action to delete a directory.

```
use Tobento\Service\Migration\Action\DirDelete;

$action = new DirDelete(
    dir: 'dir/to/store/views/blog/',
    name: 'A unique name', // or null
    description: 'Blog view files uninstalled.',
    type: 'keyword', // (optional)
);

var_dump($action->getDir());
// string(24) "dir/to/store/views/blog/"

var_dump($action->description());
// string(28) "Blog view files uninstalled."

var_dump($action->type());
// string(7) "keyword"
```

### Fail

[](#fail)

The `Fail::class` does always fail on action process, throwing a `ActionFailedException::class`, which might be useful in some cases.

```
use Tobento\Service\Migration\Action\Fail;
use Tobento\Service\Migration\ActionFailedException;

$action = new Fail(
    failMessage: 'message',
    name: 'A unique name', // or null
    description: 'Some description.', // (optional)
    type: 'keyword', // (optional)
);

$action->process();
// throws ActionFailedException with the specified fail message.
```

### Files Copy

[](#files-copy)

Use the FilesCopy::class action to copy files to another directory.

```
use Tobento\Service\Migration\Action\FilesCopy;

$action = new FilesCopy(
    files: [
        'dir/to/store/config/' => [
            'dir/blog/config/blog.php',
        ],
    ],
    overwrite: true, // if to overwrite existing files (default true)
    name: 'A unique name', // or null
    description: 'Blog configuration files installed.',
    type: 'keyword', // (optional)
);

var_dump($action->getFiles());
// array(1) { ["dir/to/store/config/"]=> array(1) { [0]=> string(24) "dir/blog/config/blog.php" } }

// only available after processing the action.
var_dump($action->getCopiedFiles());
// array(0) { }

// only available after processing the action.
var_dump($action->getSkippedFiles());
// array(0) { }

var_dump($action->description());
// string(35) "Blog configuration files installed."

var_dump($action->type());
// string(7) "keyword"
```

### Files Delete

[](#files-delete)

Use the FilesDelete::class action to delete files.

```
use Tobento\Service\Migration\Action\FilesDelete;

$action = new FilesDelete(
    files: [
        'dir/to/store/config/' => [
            'blog.php',
        ],
    ],
    name: 'A unique name', // or null
    description: 'Blog configuration files uninstalled.',
    type: 'keyword', // (optional)
);

var_dump($action->getFiles());
// array(1) { ["dir/to/store/config/"]=> array(1) { [0]=> string(8) "blog.php" } }

// only available after processing the action.
var_dump($action->getDeletedFiles());
// array(0) { }

var_dump($action->description());
// string(37) "Blog configuration files uninstalled."

var_dump($action->type());
// string(7) "keyword"
```

### File String Replacer

[](#file-string-replacer)

Use the FileStringReplacer::class to replace strings from a file.

```
use Tobento\Service\Migration\Action\FileStringReplacer;

$action = new FileStringReplacer(
    file: 'dir/config/http.php',
    replace: [
        '{key1}' => 'value1',
        '{key2}' => 'value2',
    ],
    name: 'A unique name', // or null
    description: 'Strings replaced.',
    type: 'keyword', // (optional)
);

var_dump($action->getFile());
// string(19) "dir/config/http.php"

var_dump($action->getReplace());
// array(2) { ["{key1}"]=> string(6) "value1" ["{key2}"]=> string(6) "value2" }

var_dump($action->description());
// string(17) "Strings replaced."

var_dump($action->type());
// string(7) "keyword"
```

### Null

[](#null)

The `NullAction::class` does nothing at all, which might be useful in some cases.

```
use Tobento\Service\Migration\Action\NullAction;

$action = new NullAction(
    name: 'A unique name', // or null
    description: 'Some description.', // (optional)
    type: 'keyword', // (optional)
);
```

### PDO Exec

[](#pdo-exec)

Use the PdoExec::class to execute pdo statements.

```
use Tobento\Service\Migration\Action\PdoExec;

$action = new PdoExec(
    pdo: $pdo,
    statements: [
        "CREATE TABLE IF NOT EXISTS blog (
            id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
            active tinyint(1) UNSIGNED NOT NULL DEFAULT '0',
            PRIMARY KEY (id)
        ) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci AUTO_INCREMENT=0",
    ],
    name: 'A unique name', // or null
    description: 'Blog database tables installed.',
    type: 'keyword', // (optional)
);

var_dump($action->getStatements());
// array

var_dump($action->description());
// string(31) "Blog database tables installed."

var_dump($action->type());
// string(7) "keyword"
```

### Custom Actions

[](#custom-actions)

Below is an example of writing a custom action.

```
use Tobento\Service\Migration\ActionInterface;
use Tobento\Service\Migration\ActionFailedException;

class CustomAction implements ActionInterface
{
    /**
     * Process the action.
     *
     * @return void
     * @throws ActionFailedException
     */
    public function process(): void
    {
        // process the action
    }

    /**
     * Returns a name of the action.
     *
     * @return string
     */
    public function name(): string
    {
        return $this::class;
    }

    /**
     * Returns a description of the action.
     *
     * @return string
     */
    public function description(): string
    {
        return 'Action Description';
    }

    /**
     * Returns the type of the action.
     *
     * @return string
     */
    public function type(): string
    {
        return 'keyword';
    }

    /**
     * Returns the processed data information.
     *
     * @return array
     */
    public function processedDataInfo(): array
    {
        return [];
    }
}
```

### Process Actions

[](#process-actions)

You might process an action without using the migrator.

```
use Tobento\Service\Migration\Action\DirCopy;
use Tobento\Service\Migration\ActionFailedException;

$action = new DirCopy(
    dir: 'dir/blog/views/',
    destDir: 'dir/to/store/views/blog/',
    description: 'Blog view files installed.',
);

try {
    $action->process();
} catch (ActionFailedException $e) {
    // Handle exception.
}
```

Migrator
--------

[](#migrator)

### Create Migrator

[](#create-migrator)

```
use Tobento\Service\Migration\Migrator;
use Tobento\Service\Migration\MigratorInterface;
use Tobento\Service\Migration\AutowiringMigrationFactory;
use Tobento\Service\Migration\MigrationJsonFileRepository;
use Tobento\Service\Container\Container;

// Any PSR-11 container
$container = new Container();

// Create migrator.
$migrator = new Migrator(
    new AutowiringMigrationFactory($container),
    new MigrationJsonFileRepository('private/dir/migrations/'),
);

var_dump($migrator instanceof MigratorInterface);
// bool(true)
```

### Install Migration

[](#install-migration)

```
use Tobento\Service\Migration\MigrationResultInterface;
use Tobento\Service\Migration\MigrationInstallException;

try {
    $result = $migrator->install(AnyMigration::class);

    var_dump($result instanceof MigrationResultInterface);
    // bool(true)

} catch (MigrationInstallException $e) {
    // Handle exception.
}
```

You might want to uninstall if the install has failed.

```
use Tobento\Service\Migration\MigrationResultInterface;
use Tobento\Service\Migration\MigrationInstallException;
use Tobento\Service\Migration\MigrationUninstallException;

try {
    $result = $migrator->install(AnyMigration::class);
} catch (MigrationInstallException $e) {
    try {
        $result = $migrator->uninstall(AnyMigration::class);
    } catch (MigrationUninstallException $e) {
        // Handle exception.
    }
}
```

### Uninstall Migration

[](#uninstall-migration)

```
use Tobento\Service\Migration\MigrationResultInterface;
use Tobento\Service\Migration\MigrationUninstallException;

try {
    $result = $migrator->uninstall(AnyMigration::class);

    var_dump($result instanceof MigrationResultInterface);
    // bool(true)

} catch (MigrationUninstallException $e) {
    // Handle exception.
}
```

You might want to uninstall only if the migration has been installed before.

```
use Tobento\Service\Migration\MigrationResultInterface;
use Tobento\Service\Migration\MigrationUninstallException;

if ($migrator->isInstalled(\App\Blog\AnyMigration::class))
{
    try {
        $result = $migrator->uninstall(AnyMigration::class);
    } catch (MigrationUninstallException $e) {
        // Handle exception.
    }
}
```

### Get Installed Migrations

[](#get-installed-migrations)

```
$migrations = $migrator->getInstalled();

// get only from specific.
$migrations = $migrator->getInstalled(namespace: 'App\Blog\');

```

### Migration Result

[](#migration-result)

The migrator install and uninstall method returns the migration result object on success:

```
use Tobento\Service\Migration\MigrationResultInterface;
use Tobento\Service\Migration\MigrationInterface;

$result = $migrator->install(AnyMigration::class);
var_dump($result instanceof MigrationResultInterface);
// bool(true)

$result = $migrator->uninstall(AnyMigration::class);
var_dump($result instanceof MigrationResultInterface);
// bool(true)
```

**Get the migration processed**

```
use Tobento\Service\Migration\MigrationInterface;

// Get the migration processed:
var_dump($result->migration() instanceof MigrationInterface);
// bool(true)
```

**Get the migration actions processed**

```
use Tobento\Service\Migration\ActionsInterface;

// Get the migration actions processed:
var_dump($result->actions() instanceof ActionsInterface);
// bool(true)

// You might want to iterate over the actions.
foreach($result->actions() as $action)
{
    $description = $action->description();
}
```

### Migration Results

[](#migration-results)

You might want to store your results as to get it later to show the migrations processed.

```
use Tobento\Service\Migration\Migrator;
use Tobento\Service\Migration\MigratorInterface;
use Tobento\Service\Migration\AutowiringMigrationFactory;
use Tobento\Service\Migration\MigrationJsonFileRepository;
use Tobento\Service\Migration\MigrationInstallException;
use Tobento\Service\Migration\MigrationResults;
use Tobento\Service\Migration\MigrationResultsInterface;
use Tobento\Service\Migration\MigrationResults;
use Tobento\Service\Container\Container;

// Any PSR-11 container
$container = new Container();

// Create migrator.
$migrator = new Migrator(
    new AutowiringMigrationFactory($container),
    new MigrationJsonFileRepository('private/dir/migrations/'),
);

// MigrationResults implementation.
$container->set(MigrationResultsInterface::class, function() {
    return new MigrationResults();
});

try {
    $result = $migrator->install(AnyMigration::class);

    // Add result.
    $container->get(MigrationResultsInterface::class)->add($result);

} catch (MigrationInstallException $e) {
    // Handle exception.
}

// Somewhere later, do something with the results.
$results = $container->get(MigrationResultsInterface::class)->all();
```

Credits
=======

[](#credits)

- [Tobias Strub](https://www.tobento.ch)
- [All Contributors](../../contributors)

###  Health Score

45

—

FairBetter than 92% of packages

Maintenance63

Regular maintenance activity

Popularity15

Limited adoption so far

Community14

Small or concentrated contributor base

Maturity75

Established project with proven stability

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~102 days

Recently: every ~127 days

Total

15

Last Release

227d ago

Major Versions

1.x-dev → 2.02025-09-25

PHP version history (2 changes)1.0.0PHP &gt;=8.0

2.0PHP &gt;=8.4

### Community

Maintainers

![](https://www.gravatar.com/avatar/055d6a1b5c2384bb179c75ab0b55914231d898fdc4dffeb30770f81200e52206?d=identicon)[TOBENTOch](/maintainers/TOBENTOch)

---

Top Contributors

[![tobento-ch](https://avatars.githubusercontent.com/u/16684832?v=4)](https://github.com/tobento-ch "tobento-ch (36 commits)")

---

Tags

migrationpackageinstalltobentouninstall

###  Code Quality

TestsPHPUnit

Static AnalysisPsalm

Type Coverage Yes

### Embed Badge

![Health badge](/badges/tobento-service-migration/health.svg)

```
[![Health](https://phpackages.com/badges/tobento-service-migration/health.svg)](https://phpackages.com/packages/tobento-service-migration)
```

###  Alternatives

[awssat/laravel-sync-migration

Laravel tool helps to sync migrations without refreshing the database

10923.2k](/packages/awssat-laravel-sync-migration)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
