PHPackages                             tobento/service-database - 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-database

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

tobento/service-database
========================

Creating and managing databases easily.

2.0.1(3mo ago)02374MITPHPPHP &gt;=8.4

Since Nov 28Pushed 3mo ago1 watchersCompare

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

READMEChangelog (10)Dependencies (6)Versions (23)Used By (4)

Database Service
================

[](#database-service)

With the Database Service you can create and manage databases easily.

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

[](#table-of-contents)

- [Getting started](#getting-started)
    - [Requirements](#requirements)
    - [Highlights](#highlights)
- [Documentation](#documentation)
    - [Databases](#databases)
        - [Create Databases](#create-databases)
        - [Add Databases](#add-databases)
        - [Get Database](#get-database)
        - [Default Databases](#default-databases)
    - [PDO Database](#pdo-database)
        - [Create PDO Database](#create-pdo-database)
        - [Pdo Database Factory](#pdo-database-factory)
        - [Using PDO Database](#using-pdo-database)
    - [Migration](#migration)
        - [Table Schema](#table-schema)
            - [Column Types](#column-types)
            - [Rename And Drop](#rename-and-drop)
            - [Column Parameters](#column-parameters)
            - [Indexes](#indexes)
            - [Foreign Keys](#foreign-keys)
            - [Items and Seeding](#items-and-seeding)
                - [Items](#items)
                - [Item Factory](#item-factory)
                - [Json File Items](#json-file-items)
            - [Table Factory](#table-factory)
            - [Column Factory](#column-factory)
            - [Index Factory](#index-factory)
        - [Processors](#processors)
            - [Pdo MySql Processor](#pdo-mysql-processor)
            - [Pdo Sqlite Processor](#pdo-sqlite-processor)
            - [Stack Processor](#stack-processor)
        - [Storages](#storages)
            - [Pdo MySql Storage](#pdo-mysql-storage)
            - [Pdo Sqlite Storage](#pdo-sqlite-storage)
            - [Stack Storage](#stack-storage)
            - [Custom Storage](#custom-storage)
        - [Security](#security)
        - [Migrator](#migrator)
            - [Create Migration](#create-migration)
            - [Create Migration Seeder](#create-migration-seeder)
            - [Install And Uninstall Migration](#install-and-uninstall-migration)
- [Credits](#credits)

---

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

[](#getting-started)

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

```
composer require tobento/service-database

```

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

[](#requirements)

- PHP 8.4 or greater

Highlights
----------

[](#highlights)

- Framework-agnostic, will work with any project
- Decoupled design
- Managing Databases
- Simple PDO Database Wrapper
- Migration support with table schema builder and seeding items

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

[](#documentation)

Databases
---------

[](#databases)

### Create Databases

[](#create-databases)

```
use Tobento\Service\Database\Databases;
use Tobento\Service\Database\DatabasesInterface;

$databases = new Databases();

var_dump($databases instanceof DatabasesInterface);
// bool(true)
```

### Add Databases

[](#add-databases)

**add**

```
use Tobento\Service\Database\Databases;
use Tobento\Service\Database\PdoDatabase;
use Tobento\Service\Database\DatabaseInterface;
use PDO;

$databases = new Databases();

$database = new PdoDatabase(
    pdo: new PDO('sqlite::memory:'),
    name: 'name',
);

var_dump($database instanceof DatabaseInterface);
// bool(true)

$databases->add($database);
```

**register**

You may use the register method to only create database if requested.

```
use Tobento\Service\Database\Databases;
use Tobento\Service\Database\PdoDatabase;
use Tobento\Service\Database\DatabaseInterface;
use PDO;

$databases = new Databases();

$databases->register(
    'name',
    function(string $name): DatabaseInterface {
        return new PdoDatabase(
            new PDO('sqlite::memory:'),
            $name
        );
    }
);
```

### Get Database

[](#get-database)

If the database does not exist or could not get created it throws a DatabaseException.

```
use Tobento\Service\Database\DatabaseInterface;
use Tobento\Service\Database\DatabaseException;

$database = $databases->get('name');

var_dump($database instanceof DatabaseInterface);
// bool(true)

$databases->get('unknown');
// throws DatabaseException
```

You may use the **has** method to check if a database exists.

```
var_dump($databases->has('name'));
// bool(false)
```

### Default Databases

[](#default-databases)

You may add default databases for your application design.

```
use Tobento\Service\Database\Databases;
use Tobento\Service\Database\PdoDatabase;
use Tobento\Service\Database\DatabaseInterface;
use Tobento\Service\Database\DatabaseException;
use PDO;

$databases = new Databases();

$databases->add(
    new PdoDatabase(new PDO('sqlite::memory:'), 'sqlite')
);

// add default
$databases->addDefault(name: 'primary', database: 'sqlite');

// get default database for the specified name.
$primaryDatabase = $databases->default('primary');

var_dump($primaryDatabase instanceof DatabaseInterface);
// bool(true)

var_dump($databases->hasDefault('primary'));
// bool(true)

var_dump($databases->getDefaults());
// array(1) { ["primary"]=> string(6) "sqlite" }

$databases->default('unknown');
// throws DatabaseException
```

PDO Database
------------

[](#pdo-database)

### Create PDO Database

[](#create-pdo-database)

```
use Tobento\Service\Database\PdoDatabase;
use Tobento\Service\Database\DatabaseInterface;
use Tobento\Service\Database\PdoDatabaseInterface;
use PDO;

$database = new PdoDatabase(
    pdo: new PDO('sqlite::memory:'),
    name: 'sqlite',
);

var_dump($database instanceof DatabaseInterface);
// bool(true)

var_dump($database instanceof PdoDatabaseInterface);
// bool(true)
```

### Pdo Database Factory

[](#pdo-database-factory)

You may use the factory to easily create a database.

**createDatabase**

```
use Tobento\Service\Database\PdoDatabaseFactory;
use Tobento\Service\Database\DatabaseFactoryInterface;
use Tobento\Service\Database\PdoDatabaseInterface;
use Tobento\Service\Database\DatabaseInterface;
use Tobento\Service\Database\DatabaseException;
use PDO;

$factory = new PdoDatabaseFactory();

var_dump($factory instanceof DatabaseFactoryInterface);
// bool(true)

$database = $factory->createDatabase(
    name: 'mysql',
    config: [
        'driver' => 'mysql',
        'host' => 'localhost',
        'port' => null,
        'database' => 'db_name',
        'charset' => 'utf8mb4',
        'username' => 'root',
        'password' => '',
        'options' => [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_EMULATE_PREPARES => false,
        ],
    ],
);
// throws DatabaseException on failure

var_dump($database instanceof PdoDatabaseInterface);
// bool(true)

var_dump($database instanceof DatabaseInterface);
// bool(true)

// with dsn parameter
$database = $factory->createDatabase(
    name: 'sqlite',
    config: [
        'dsn' => 'sqlite::memory:',
        'username' => '',
        'password' => '',
        'options' => [],
    ],
);
```

**createPdo**

```
use Tobento\Service\Database\PdoDatabaseFactory;
use Tobento\Service\Database\PdoFactoryInterface;
use Tobento\Service\Database\DatabaseException;
use PDO;

$factory = new PdoDatabaseFactory();

var_dump($factory instanceof PdoFactoryInterface);
// bool(true)

$pdo = $factory->createPdo(
    name: 'sqlite',
    config: [
        'dsn' => 'sqlite::memory:',
        'username' => '',
        'password' => '',
        'options' => [],
    ],
);
// throws DatabaseException on failure

var_dump($pdo instanceof PDO);
// bool(true)
```

### Using PDO Database

[](#using-pdo-database)

Use the **execute** method to excute any prepared statements.

```
use PDOStatement;

$statement = $database->execute(
    statement: 'ALTER TABLE products ADD color varchar(60)'
);

var_dump($statement instanceof PDOStatement);
// bool(true)
```

**select**

```
$products = $database->execute(
    statement: 'SELECT title FROM products WHERE color = ?',
    bindings: ['blue']
)->fetchAll();

// with named parameters, order free
$products = $database->execute(
    'SELECT title FROM products WHERE color = :color',
    ['color' => 'blue']
)->fetchAll();
```

**insert**

```
$database->execute(
    statement: 'INSERT INTO shop_products (name, color) VALUES (?, ?)',
    bindings: ['Shirt', 'blue'],
);

// you might return the number of rows affected:
$rowsAffected = $database->execute(
    'INSERT INTO shop_products (name, color) VALUES (?, ?)',
    ['Shirt', 'blue']
)->rowCount();

var_dump($rowsAffected);
// int(1)
```

**update**

Execute a insert statement returning the number of rows affected.

```
$rowsAffected = $database->execute(
    statement: 'UPDATE products SET name = ? WHERE color = ?',
    bindings: ['Shirt', 'blue']
)->rowCount();

var_dump($rowsAffected);
// int(2)
```

**delete**

Execute a delete statement returning the number of rows affected.

```
$rowsAffected = $database->execute(
    statement: 'DELETE FROM shop_products WHERE color = ?',
    bindings: ['blue']
)->rowCount();

var_dump($rowsAffected);
// int(2)
```

**transaction**

You may use the transaction method to run a set of database operations within a transaction. If an exception is thrown within the transaction closure, the transaction will automatically be rolled back. If the closure executes successfully, the transaction will automatically be committed.

```
use Tobento\Service\Database\PdoDatabaseInterface;

$database->transaction(function(PdoDatabaseInterface $db): void {

    $db->execute(
        'UPDATE products SET active = ? WHERE color = ?',
        [true, 'red']
    );

    $db->execute(
        'UPDATE products SET active = ? WHERE color = ?',
        [false, 'bar']
    );
});
```

**commit**

```
$database->begin();

// your queries

$database->commit();
```

**rollback**

```
$database->begin();

// your queries

$database->rollback();
```

**supportsNestedTransactions**

```
var_dump($database->supportsNestedTransactions());
// bool(true)
```

**PDO**

```
use PDO;

var_dump($database->pdo() instanceof PDO);
// bool(true)
```

Migration
---------

[](#migration)

### Table Schema

[](#table-schema)

Table schemas are used by [Processors](#processors) and [Storages](#storages) for migration processing.

```
use Tobento\Service\Database\Schema\Table;

$table = new Table(name: 'products');
$table->primary('id');
$table->string('name', 100)->nullable(false)->default('');
$table->bool('active', true);
```

#### Column Types

[](#column-types)

**Available Types**

TypeParametersLengthableNullableDefaultableUnsignableDescription**primary**name: 'column'yesnonoyesUsually mapped as int, auto-incrementing and added as primary index column.**bigPrimary**name: 'column'yesnonoyesUsually mapped as bigint, auto-incrementing and added as primary index column.**blob**name: 'column'noyesyesno-**bool**name: 'column'nonoyesnoSome databases will store it as tinyint (1/0).**int**name: 'column', length: 11yesyesyesyes-**tinyInt**name: 'column', length: 1yesyesyesyes-**bigInt**name: 'column', length: 20yesyesyesyes-**char**name: 'column', length: 255yesyesyesno-**string**name: 'column', length: 255yesyesyesno-**text**name: 'column'noyesyesno-**double**name: 'column'noyesyesno-**float**name: 'column'noyesyesno-**decimal**name: 'column', precision: 10, scale:0noyesyesno-**datetime**name: 'column'noyesyesno-**date**name: 'column'noyesyesno-**time**name: 'column'noyesyesno-**timestamp**name: 'column'noyesyesno-**json**name: 'column'noyesyesno-**Lengthable**

To set a column length.

```
$table->string('name')->length(21);
```

**Nullable**

To set column as NOT NULL, use nullable method with false as parameter:

```
$table->string('name')->nullable(false);
```

**Defaultable**

To set a default value for the column.

```
$table->bool('name')->default(true);
```

**Unsignable**

To set column as UNSIGNED.

```
$table->int('name')->unsigned(true);
```

**primary / bigPrimary**

Primary and bigPrimary columns will only be set while creation.

#### Rename and Drop

[](#rename-and-drop)

**renameColumn**

```
$table->renameColumn('column', 'new_column');
```

**dropColumn**

```
$table->dropColumn('column');
```

**renameTable**

```
$table->renameTable('new_name');
```

**dropTable**

```
$table->dropTable();
```

**truncate**

```
$table->truncate();
```

#### Column Parameters

[](#column-parameters)

**charset**

You might set the charset of the column.

```
$table->string('column')->parameter('charset', 'utf8mb4');
```

**collation**

You might set the collation of the column.

```
$table->string('column')->parameter('collation', 'utf8mb4_roman_ci');
```

#### Indexes

[](#indexes)

**Simple index**

```
$table->index('index_name')->column('name');
```

**Compound index**

```
$table->index('index_name')->column('name', 'another_name');
```

**Simple unique index**

```
$table->index('index_name')->column('name')->unique();
```

**Compound unique index**

```
$table->index('index_name')->column('name', 'another_name')->unique();
```

**Primary index**

```
$table->index()->column('name')->primary();

// drop primary index:
$table->index()->column('name')->primary()->drop();
```

Note

Sqlite does not support adding or dropping primary key indexes!

**Rename index**

```
$table->index('index_name')->rename('new_name');
```

**Drop index**

```
$table->index('index_name')->drop();
```

#### Foreign Keys

[](#foreign-keys)

No supported yet!

#### Items and Seeding

[](#items-and-seeding)

##### Items

[](#items)

```
use Tobento\Service\Database\Schema\ItemsInterface;

$items = $table->items(iterable: [
    ['name' => 'Foo', 'active' => true],
    ['name' => 'Bar', 'active' => true],
    // ...
])
->chunk(length: 100)
->useTransaction(false) // default is true
->forceInsert(true); // default is false

var_dump($items instanceof ItemsInterface);
// bool(true)
```

**chunk**

You may play around with the chunk **length** parameter for speed improvements while having many items.

**useTransaction**

If set to **true**, it uses transaction while proccessing if the database supports it.

**forceInsert**

If set to **true**, it will always inserts the items, otherwise they will only be inserted if there there are not items yet.

##### Item Factory

[](#item-factory)

You may use the item factory iterator to seed items and use the [Seeder Service](https://github.com/tobento-ch/service-seeder) to generate fake data.

```
use Tobento\Service\Iterable\ItemFactoryIterator;
use Tobento\Service\Seeder\Str;
use Tobento\Service\Seeder\Arr;

$table->items(new ItemFactoryIterator(
    factory: function(): array {
        return [
            'name' => Str::string(10),
            'color' => Arr::item(['green', 'red', 'blue']),
        ];
    },
    create: 1000000 // create 1 million items
))
->chunk(length: 10000)
->useTransaction(false) // default is true
->forceInsert(true); // default is false
```

##### Json File Items

[](#json-file-items)

```
use Tobento\Service\Iterable\JsonFileIterator;
use Tobento\Service\Iterable\ModifyIterator;

$iterator = new JsonFileIterator(
    file: 'private/src/countries.json',
);

// you may use the modify iterator:
$iterator = new ModifyIterator(
    iterable: $iterator,
    modifier: function(array $item): array {
        return [
          'iso' => $item['iso'] ?? '',
          'name' => $item['country'] ?? '',
        ];
    }
);

$table->items($iterator)
      ->chunk(length: 100)
      ->useTransaction(true) // default is true
      ->forceInsert(false); // default is false
```

### Table Factory

[](#table-factory)

You may use the table factory to create a table.

```
use Tobento\Service\Database\Schema\TableFactoryInterface;
use Tobento\Service\Database\Schema\TableFactory;
use Tobento\Service\Database\Schema\Table;

$tableFactory = new TableFactory();

var_dump($tableFactory instanceof TableFactoryInterface);
// bool(true)

$table = $tableFactory->createTable(name: 'users');

var_dump($table instanceof Table);
// bool(true)
```

### Column Factory

[](#column-factory)

You may use the column factory to create a column.

```
use Tobento\Service\Database\Schema\ColumnFactoryInterface;
use Tobento\Service\Database\Schema\ColumnFactory;

$columnFactory = new ColumnFactory();

var_dump($columnFactory instanceof ColumnFactoryInterface);
// bool(true)
```

**createColumn**

```
use Tobento\Service\Database\Schema\ColumnFactory;
use Tobento\Service\Database\Schema\ColumnInterface;
use Tobento\Service\Database\Schema\CreateColumnException;

try {
    $column = new ColumnFactory()->createColumn(type: 'int', name: 'foo');

    var_dump($column instanceof ColumnInterface);
    // bool(true)

} catch(CreateColumnException $e) {
    //
}
```

Check out the supported [Column Types](#column-types) for its type name.

**createColumnFromArray**

```
use Tobento\Service\Database\Schema\ColumnFactory;
use Tobento\Service\Database\Schema\ColumnInterface;
use Tobento\Service\Database\Schema\CreateColumnException;

try {
    $column = new ColumnFactory()->createColumnFromArray([
        'type' => 'int',
        'name' => 'foo',
    ]);

    var_dump($column instanceof ColumnInterface);
    // bool(true)

} catch(CreateColumnException $e) {
    //
}
```

Lengthable, Nullable, Defaultable, Unsignable and Parameters column definitions:

```
use Tobento\Service\Database\Schema\ColumnFactory;

$column = new ColumnFactory()->createColumnFromArray([
    'type' => 'int',
    'name' => 'foo',

    'length' => 99,
    'nullable' => false,
    'default' => 'value',
    'unsigned' => true,

    'parameters' => [
        'charset' => 'utf8mb4',
        'collation' => 'utf8mb4_roman_ci',
    ],

    // Decimal column
    'precision' => 8,
    'scale' => 4,
]);
```

### Index Factory

[](#index-factory)

```
use Tobento\Service\Database\Schema\IndexFactoryInterface;
use Tobento\Service\Database\Schema\IndexFactory;

$indexFactory = new IndexFactory();

var_dump($indexFactory instanceof IndexFactoryInterface);
// bool(true)
```

**createIndex**

```
use Tobento\Service\Database\Schema\IndexFactory;
use Tobento\Service\Database\Schema\IndexInterface;
use Tobento\Service\Database\Schema\CreateIndexException;

try {
    $index = new IndexFactory()->createIndex(name: 'foo');

    var_dump($index instanceof IndexInterface);
    // bool(true)

} catch(CreateIndexException $e) {
    //
}
```

**createIndexFromArray**

```
use Tobento\Service\Database\Schema\IndexFactory;
use Tobento\Service\Database\Schema\IndexInterface;
use Tobento\Service\Database\Schema\CreateIndexException;

try {
    $index = new IndexFactory()->createIndexFromArray([
        'name' => 'foo',
    ]);

    var_dump($index instanceof IndexInterface);
    // bool(true)

} catch(CreateIndexException $e) {
    //
}
```

Other parameter definitions:

```
use Tobento\Service\Database\Schema\IndexFactory;

$index = new IndexFactory()->createIndexFromArray([
    'name' => 'foo',

    'column' => 'name',
    // or multiple
    'column' => ['name', 'another_name'],

    'unique' => true,
    'primary' => true,

    'rename' => 'newname',
    'drop' => true,
]);
```

### Processors

[](#processors)

Processors are used to process the table on the specified database.

#### Pdo MySql Processor

[](#pdo-mysql-processor)

The proccessor will automatically determine if to add or modify table columns and indexes.

```
use Tobento\Service\Database\Processor\PdoMySqlProcessor;
use Tobento\Service\Database\Processor\ProcessorInterface;
use Tobento\Service\Database\Processor\ProcessException;
use Tobento\Service\Database\PdoDatabaseInterface;
use Tobento\Service\Database\Schema\Table;

$processor = new PdoMySqlProcessor();

var_dump($processor instanceof ProcessorInterface);
// bool(true)

try {
    $processor->process(
        $table, // Table
        $database // PdoDatabaseInterface
    );
} catch (ProcessException $e) {
    // Handle exception.
}
```

You may create a [Custom Storage](#custom-storage) for the processor. The default storage [Pdo MySql Storage](#pdo-mysql-storage) will query the database to create the current table as to determine modifications.

```
use Tobento\Service\Database\Processor\PdoMySqlProcessor;

$processor = new PdoMySqlProcessor(new CustomStorage());
```

#### Pdo Sqlite Processor

[](#pdo-sqlite-processor)

The proccessor will automatically determine if to add or modify table columns and indexes.

```
use Tobento\Service\Database\Processor\PdoSqliteProcessor;
use Tobento\Service\Database\Processor\ProcessorInterface;
use Tobento\Service\Database\Processor\ProcessException;
use Tobento\Service\Database\PdoDatabaseInterface;
use Tobento\Service\Database\Schema\Table;

$processor = new PdoSqliteProcessor();

var_dump($processor instanceof ProcessorInterface);
// bool(true)

try {
    $processor->process(
        $table, // Table
        $database // PdoDatabaseInterface
    );
} catch (ProcessException $e) {
    // Handle exception.
}
```

You may create a [Custom Storage](#custom-storage) for the processor. The default storage [Pdo Sqlite Storage](#pdo-sqlite-storage) will query the database to create the current table as to determine modifications.

```
use Tobento\Service\Database\Processor\PdoSqliteProcessor;

$processor = new PdoSqliteProcessor(new CustomStorage());
```

#### Stack Processor

[](#stack-processor)

You may use the "stack processor" to support multiple databases. Only the first processor which supports the specified database will process the action.

```
use Tobento\Service\Database\Processor\Processors;
use Tobento\Service\Database\Processor\PdoMySqlProcessor;
use Tobento\Service\Database\Processor\ProcessorInterface;
use Tobento\Service\Database\Processor\ProcessException;

$processors = new Processors(
    new PdoMySqlProcessor(),
);

var_dump($processor instanceof ProcessorInterface);
// bool(true)

try {
    $processors->process($table, $database);
} catch (ProcessException $e) {
    //
}
```

### Storages

[](#storages)

Storages are used to fetch the current table or store the processed.

#### Pdo MySql Storage

[](#pdo-mysql-storage)

**fetchTable**

The storage will query the database to create the current table.

```
use Tobento\Service\Database\Processor\PdoMySqlStorage;
use Tobento\Service\Database\Processor\StorageInterface;
use Tobento\Service\Database\Processor\StorageFetchException;
use Tobento\Service\Database\PdoDatabaseInterface;
use Tobento\Service\Database\Schema\Table;

$storage = new PdoMySqlStorage();

var_dump($storage instanceof StorageInterface);
// bool(true)

try {
    $table = $storage->fetchTable(
        $database, // PdoDatabaseInterface
        'table_name'
    );

    var_dump($table instanceof Table);
    // bool(true) or NULL if table does not exist.

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

**storeTable**

No table data is stored as fetching will create the table.

```
use Tobento\Service\Database\Processor\PdoMySqlStorage;
use Tobento\Service\Database\Processor\StorageInterface;
use Tobento\Service\Database\Processor\StorageStoreException;
use Tobento\Service\Database\PdoDatabaseInterface;
use Tobento\Service\Database\Schema\Table;

$storage = new PdoMySqlStorage();

var_dump($storage instanceof StorageInterface);
// bool(true)

try {
    $storage->storeTable(
        $database, // PdoDatabaseInterface
        $table // Table
    );

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

#### Pdo Sqlite Storage

[](#pdo-sqlite-storage)

**fetchTable**

The storage will query the database to create the current table.

```
use Tobento\Service\Database\Processor\PdoSqliteStorage;
use Tobento\Service\Database\Processor\StorageInterface;
use Tobento\Service\Database\Processor\StorageFetchException;
use Tobento\Service\Database\PdoDatabaseInterface;
use Tobento\Service\Database\Schema\Table;

$storage = new PdoSqliteStorage();

var_dump($storage instanceof StorageInterface);
// bool(true)

try {
    $table = $storage->fetchTable(
        $database, // PdoDatabaseInterface
        'table_name'
    );

    var_dump($table instanceof Table);
    // bool(true) or NULL if table does not exist.

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

**storeTable**

No table data is stored as fetching will create the table.

```
use Tobento\Service\Database\Processor\PdoSqliteStorage;
use Tobento\Service\Database\Processor\StorageInterface;
use Tobento\Service\Database\Processor\StorageStoreException;
use Tobento\Service\Database\PdoDatabaseInterface;
use Tobento\Service\Database\Schema\Table;

$storage = new PdoSqliteStorage();

var_dump($storage instanceof StorageInterface);
// bool(true)

try {
    $storage->storeTable(
        $database, // PdoDatabaseInterface
        $table // Table
    );

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

#### Stack Storage

[](#stack-storage)

You may use the "stack storage" to support multiple databases. Only the first storage which supports the specified database will process the action.

```
use Tobento\Service\Database\Processor\Storages;
use Tobento\Service\Database\Processor\PdoMySqlStorage;
use Tobento\Service\Database\Processor\StorageInterface;
use Tobento\Service\Database\Processor\StorageStoreException;

$storages = new Storages(
    new PdoMySqlStorage(),
);

var_dump($storages instanceof StorageInterface);
// bool(true)

try {
    $storages->storeTable($database, $table);
} catch (StorageStoreException $e) {
    // Handle exception.
}
```

#### Custom Storage

[](#custom-storage)

You may create custom storages for proccssors or as standalone usage.

```
use Tobento\Service\Database\Processor\PdoMySqlProcessor;
use Tobento\Service\Database\Processor\StorageInterface;
use Tobento\Service\Database\Processor\StorageFetchException;
use Tobento\Service\Database\Processor\StorageStoreException;
use Tobento\Service\Database\Processor\ProcessException;
use Tobento\Service\Database\Schema\Table;
use Tobento\Service\Database\DatabaseInterface;

class CustomStorage implements StorageInterface
{
    /**
     * Returns true if the processor supports the database, otherwise false.
     *
     * @param DatabaseInterface $database
     * @return bool
     */
    public function supportsDatabase(DatabaseInterface $database): bool
    {
        return true;
    }

    /**
     * Returns the specified table if exist, otherwise null.
     *
     * @param DatabaseInterface $database
     * @param string $table The table name
     * @return null|Table
     * @throws StorageFetchException
     */
    public function fetchTable(DatabaseInterface $database, string $table): null|Table
    {
        // your logic.
        return null;
    }

    /**
     * Store the table.
     *
     * @param DatabaseInterface $database
     * @param Table $table
     * @return void
     * @throws StorageStoreException
     */
    public function storeTable(DatabaseInterface $database, Table $table): void
    {
        // your logic.
    }
}

try {
    $table = new CustomStorage()->fetchTable($database, 'table_name');
} catch (ProcessException $e) {
    // Handle exception.
}
```

### Security

[](#security)

> ⚠️ **Avoid using user provided data for creating [Table Schema](#table-schema) without a proper whitelist!**

### Migrator

[](#migrator)

You might install and use the [Migration Service](https://github.com/tobento-ch/service-migration) for migration processing.

#### Create Migration

[](#create-migration)

Create a migration class by extending the `DatabaseMigration::class`.

**Using the `registerTables` method**

You may use the `registerTables` method to register the table for the install and uninstall process.

```
use Tobento\Service\Database\Migration\DatabaseMigration;
use Tobento\Service\Database\Schema\Table;

class DbMigrations extends DatabaseMigration
{
    public function description(): string
    {
        return 'db migrations';
    }

    /**
     * Register tables used by the install and uninstall methods
     * to create the actions from.
     *
     * @return void
     */
    protected function registerTables(): void
    {
        $this->registerTable(
            table: function(): Table {
                $table = new Table(name: 'users');
                $table->primary('id');
                return $table;
            },
            database: $this->databases()->default('pdo'),
            name: 'Users',
            description: 'Users desc',
        );

        $this->registerTable(
            table: function(): Table {
                $table = new Table(name: 'products');
                $table->primary('id');
                return $table;
            },
            database: $this->databases()->default('pdo'),
        );
    }
}
```

Check out the [Table Schema](#table-schema) for its documentation.

**Using the `install` and `uninstall` methods**

You may use the `install` and `uninstall` methods for specifing the actions by your own.

```
use Tobento\Service\Database\Migration\DatabaseMigration;
use Tobento\Service\Database\Migration\DatabaseAction;
use Tobento\Service\Database\Migration\DatabaseDeleteAction;
use Tobento\Service\Database\Schema\Table;
use Tobento\Service\Migration\ActionsInterface;
use Tobento\Service\Migration\Actions;

class DbMigrations extends DatabaseMigration
{
    public function description(): string
    {
        return 'db migrations';
    }

    /**
     * Return the actions to be processed on install.
     *
     * @return ActionsInterface
     */
    public function install(): ActionsInterface
    {
        return new Actions(
            new DatabaseAction(
                processor: $this->processor,
                database: $this->databases()->default('pdo'),
                table: function(): Table {
                    $table = new Table(name: 'products');
                    $table->primary('id');
                    return $table;
                },
                name: 'Products',
                description: 'Products table installed',
            ),
        );
    }

    /**
     * Return the actions to be processed on uninstall.
     *
     * @return ActionsInterface
     */
    public function uninstall(): ActionsInterface
    {
        return $this->createDatabaseDeleteActionsFromInstall();

        // or manually:
        return new Actions(
            new DatabaseDeleteAction(
                processor: $this->processor,
                database: $this->databases()->default('pdo'),
                table: new Table(name: 'products'),
                name: 'Products',
                description: 'Products table uninstalled',
            ),
        );
    }
}
```

Check out the [Table Schema](#table-schema) for its documentation.

#### Create Migration Seeder

[](#create-migration-seeder)

First, you will need to install the [Seeder Service](https://github.com/tobento-ch/service-seeder) and bind the `SeedInterface::class` implementation to your container in order to get injected on the `DatabaseMigrationSeeder::class`.

Next, create a migration seeder class by extending the `DatabaseMigrationSeeder::class`.

Then use the `registerTables` method to register the table for the install process.

```
use Tobento\Service\Database\Migration\DatabaseMigrationSeeder;
use Tobento\Service\Database\Schema\Table;
use Tobento\Service\Iterable\ItemFactoryIterator;

class DbMigrationsSeeder extends DatabaseMigrationSeeder
{
    public function description(): string
    {
        return 'db migrations seeding';
    }

    /**
     * Register tables used by the install method
     * to create the actions from.
     * The uninstall method returns empty actions.
     *
     * @return void
     */
    protected function registerTables(): void
    {
        $this->registerTable(
            table: function(): Table {
                $table = new Table(name: 'users');
                // no need to specifiy columns again
                // if you the table migrated before.

                // seeding:
                $table->items(new ItemFactoryIterator(
                    factory: function(): array {
                        return [
                            'name' => $this->seed->fullname(),
                            'email' => $this->seed->email(),
                        ];
                    },
                    create: 10000
                ))
                ->chunk(length: 2000)
                ->useTransaction(false) // default is true
                ->forceInsert(true); // default is false

                return $table;
            },
            database: $this->databases()->default('pdo'),
        );
    }
}
```

Check out the [Seeder Service](https://github.com/tobento-ch/service-seeder) for its documentation.

Check out the [Table Schema](#table-schema) for its documentation.

#### Install And Uninstall Migration

[](#install-and-uninstall-migration)

```
$result = $migrator->install(DbMigrations::class);

$result = $migrator->uninstall(DbMigrations::class);
```

Check out the following migration service documentation to learn more about it.

- [Create Migrator](https://github.com/tobento-ch/service-migration#create-migrator)
- [Install Migration](https://github.com/tobento-ch/service-migration#install-migration)
- [Uninstall Migration](https://github.com/tobento-ch/service-migration#uninstall-migration)

Credits
=======

[](#credits)

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

###  Health Score

49

—

FairBetter than 95% of packages

Maintenance80

Actively maintained with recent releases

Popularity14

Limited adoption so far

Community13

Small or concentrated contributor base

Maturity77

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 ~69 days

Recently: every ~33 days

Total

23

Last Release

105d ago

Major Versions

1.x-dev → 2.02025-09-26

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 (53 commits)")

---

Tags

packagedatabasepdodatabase-migrationtobentodatabase-manager

###  Code Quality

TestsPHPUnit

Static AnalysisPsalm

Type Coverage Yes

### Embed Badge

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

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

###  Alternatives

[paragonie/easydb

Easy-to-use database abstraction

744273.4k23](/packages/paragonie-easydb)[lincanbin/php-pdo-mysql-class

A PHP MySQL PDO class similar to the Python MySQLdb, which supports iterator and parameter binding when using 'WHERE IN' statement.

2386.4k](/packages/lincanbin-php-pdo-mysql-class)[bephp/activerecord

micro activerecord library in PHP(only 400 lines with comments), support chain calls and relations(HAS\_ONE, HAS\_MANY, BELONGS\_TO).

1202.1k2](/packages/bephp-activerecord)

PHPackages © 2026

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