GNU Lesser General Public License v3.0

技术解读

以下内容由 GitRead 中文社区 编辑团队基于项目 README 与官方资料整理, 结合中文开发者的常见使用场景补充技术选型建议。具体以项目官方文档为准。

声明式数据库迁移的另一种解法:拆解 aimeos/upscheme

项目背景与痛点

PHP 生态里的数据库迁移工具已经相当成熟:Doctrine Migrations、Phinx、Laravel Migrations 各有拥趸。但它们几乎都共享同一套模型——命令式迁移。开发者手写 up() / down(),框架用一张版本表按序执行,状态由“执行到第几个脚本”来定义。

这套模型在项目早期很舒服,规模一大就开始暴露问题:

  • 迁移脚本只增不减,几十上百个文件后没人敢删,历史包袱永久固化;
  • 不同分支各自生成迁移,版本号顺序冲突,合并时互相踩踏;
  • 多数据库支持意味着同一份变更要写 MySQL、PostgreSQL、SQL Server 好几套 SQL;
  • 最难的是存量系统接入——数据库已经跑了两三年,谁也没有耐心把历史结构倒推成一串迁移脚本。

aimeos/upscheme 选择了另一条路:声明式 Schema + 差异计算。你只描述“数据库最终应该长什么样”,由工具去对比现状、算出该执行什么 DDL。同时它提供反向工程能力,能把一个现存的数据库直接导出为 schema 定义文件。这两点组合起来,恰好命中上面列出的痛点。

核心技术揭秘

从架构上看,upscheme 并不试图重新发明数据库抽象层,而是把重活交给 Doctrine DBAL 的 SchemaManager,自己在上面叠了一层“目标态构建 + 差异比对 + 任务编排”的逻辑。

graph TD A[schema 定义文件(PHP 数组)] --> B[Schema Builder 构建目标结构] C[现有数据库] --> D[Doctrine SchemaManager 读取元数据] D --> E[当前结构快照] B --> F[Comparator 差异比对] E --> F F -->|"存在差异"| G[数据库方言适配层] F -->|"结构一致"| H[跳过执行,保证幂等] G --> I[生成 CREATE / ALTER / DROP 语句] I --> J[事务内执行结构变更] J --> K[按依赖顺序执行数据迁移 Task] K --> L[写入迁移版本记录]

流程里的第一个技术难点在 Schema Builder。声明式定义看起来只是配置数组,但要把它还原成 Doctrine 的 Schema 对象,需要处理列类型映射、默认值语义、自增与序列、索引命名规则、外键约束名等一堆细节。索引名尤其麻烦:MySQL 的索引名只在表内唯一,PostgreSQL 却在 schema 级唯一,命名策略不一致会导致每次 diff 都误判为“索引已变更”,从而反复重建。

第二个难点是 Comparator 的精度。Doctrine 自带的比较器覆盖了大部分结构性差异,但对注释、字符集、排序规则、默认值表达式的比较并不完备。声明式迁移的致命弱点就在这里:diff 一旦误报,就会在每次部署时生成无意义的 ALTER 语句,轻则噪声,重则在大表上触发锁表。upscheme 的价值很大程度上取决于它对这些边缘字段的收敛程度。

第三个难点是 方言适配。SQLite 至今不支持直接 DROP COLUMN(旧版本)或修改列定义,标准做法是“建临时表 → 拷数据 → 删原表 → 重命名”,整个过程还要处理外键开关。MySQL 的 DDL 会隐式提交,无法包在事务里回滚。这些差异不会出现在 README 里,却是真正决定工具能否在生产落地的部分。

最后是任务编排。结构变更和数据迁移被拆成两类 Task,通过声明式依赖(before / after)排序,而不是靠文件编号。这一点与 Aimeos 自身的 setup 体系一脉相承,也让“先加列、再回填数据、最后加非空约束”这类典型三段式操作有了自然的表达方式。

功能亮点与差异

反向工程是它最锋利的刀。 一行命令把现有库导成 schema 定义,意味着遗留项目可以零成本切换到声明式管理,不需要补写历史迁移。Phinx 和 Doctrine Migrations 在这一步基本只能靠人工。

幂等性带来的部署确定性。 命令式迁移依赖版本表,一旦有人手工改过线上库,状态就失真;声明式 diff 每次都以真实结构为准,重复执行无副作用,特别适合容器化环境和多环境漂移的排查。

一套定义,多库通吃。 对需要同时支持 MySQL / PostgreSQL / SQLite 的开源产品(Aimeos 本身就是典型)来说,省掉的是成倍的方言维护成本。

结构与数据统一编排。 大多数迁移工具只管结构,数据回填要另写脚本;upscheme 把两者放进同一个依赖图里,顺序可控。

应用场景与落地建议

它最适合多数据库支持的开源产品或中长期演进的 SaaS,尤其是已有存量数据库、需要逐步收编的项目;也适合把“数据库结构”当作可审查代码、想在 Code Review 里看到 diff 的团队。

生产环境落地,有几条必须注意:

  1. 上线前务必先跑一次 dry-run 对比,确认 diff 结果只包含预期变更。误报的 ALTER 在千万级表上代价极高。
  2. MySQL 的 DDL 不可回滚,执行前做物理备份;大表变更优先评估 ALGORITHM=INPLACE 或走 gh-ost 之类的在线工具,不要指望迁移框架替你解决锁表。
  3. SQLite 的整表重建有写锁窗口,移动端或边缘部署场景要安排好停机或重试策略。
  4. 抽象层必然泄漏。分区表、全文索引、触发器、存储过程、生成列这些方言特性,声明式定义未必能完整表达,该手写 SQL 的地方不要硬套。
  5. 回滚策略要提前想清楚。声明式工具擅长前进,倒退通常需要保留旧 schema 定义作为“上一版目标态”,这要求你把 schema 文件纳入版本管理并打 tag。

综合评价

优点:用声明式模型换来了部署的确定性和幂等性;反向工程解决了存量系统接入这个真实痛点;多方言适配和结构/数据统一编排,对跨库产品很友好;依赖 Doctrine DBAL,学习成本低,嵌入任意 PHP 项目都不违和。

缺点:社区规模小、文档偏薄,很多行为需要读源码确认;diff 精度受限于比较器实现,注释、默认值等边缘字段可能在反复部署中产生噪声;声明式抽象对数据库方言特性覆盖有限,复杂场景仍要退回手写;缺少内建的在线 DDL 与大表治理能力,规模化生产需要额外配套。

一句话总结:它不是 Doctrine Migrations 的替代品,而是补上了“存量库纳管”和“多库结构收敛”这两块拼图。 如果你的项目正被迁移脚本的历史包袱压着,或者需要一套定义喂给多个数据库,它值得认真评估;如果只是小项目单库迭代,命令式迁移依然是更省心的选择。

关于

GitHub 原始描述(英文)

Database migrations and schema updates made easy

描述取自 GitHub 官方 API,保持仓库作者书写的原文。

语言占比

HTML
JavaScript
PHP
SCSS

README

Build Status Coverage Status License

Upscheme: Database schema updates made easy

Easy to use PHP package for updating the database schema of your application and migrate data between versions.

composer req aimeos/upscheme

Table of contents

Why Upscheme

Migrations are like version control for your database. They allow you to get the exact same state in every installation. Using Upscheme, you get:

  • one place for defining tables, columns, indexes, etc. easily
  • upgrades from any state in between to the expected schema
  • consistent, reliable and hassle-free schema upgrades
  • minimal code required for writing migrations
  • perfect solution for continuous deployments
  • best package for cloud-based PHP applications

Here’s an example of a table definition that you can adapt whenever your table layout must change. Then, Upscheme will automatically add and modify existing columns and table properties (but don’t delete anything for safety reasons):

$this->db()->table( 'test', function( $t ) {
	$t->engine = 'InnoDB';

	$t->id();
	$t->string( 'domain', 32 );
	$t->string( 'code', 64 )->opt( 'charset', 'binary', ['mariadb', 'mysql'] );
	$t->string( 'label', 255 );
	$t->int( 'pos' )->default( 0 );
	$t->smallint( 'status' );
	$t->default();

	$t->unique( ['domain', 'code'] );
	$t->index( ['status', 'pos'] );
} );

For upgrading relational database schemas, two packages are currently used most often: Doctrine DBAL and Doctrine migrations. While Doctrine DBAL does a good job in abstracting the differences of several database implementations, it’s API requires writing a lot of code. Doctrine migrations on the other site has some drawbacks which make it hard to use in all applications that support 3rd party extensions.

Doctrine DBAL drawbacks

The API of DBAL is very verbose and you need to write lots of code even for simple things. Upscheme uses Doctrine DBAL to offer an easy to use API for upgrading the database schema of your application with minimal code. For the Upscheme example above, these lines of code are the equivalent for DBAL in a migration:

$dbalManager = $conn->createSchemaManager();
$from = $manager->createSchema();
$to = $manager->createSchema();

if( $to->hasTable( 'test' ) ) {
	$table = $to->getTable( 'test' );
} else {
	$table = $to->createTable( 'test' );
}

$table->addOption( 'engine', 'InnoDB' );

$table->addColumn( 'id', 'integer', ['autoincrement' => true] );
$table->addColumn( 'domain', 'string', ['length' => 32] );

$platform = $conn->getDatabasePlatform();
if( $platform instanceof \Doctrine\DBAL\Platform\MySQLPlatform
	|| $platform instanceof \Doctrine\DBAL\Platform\MariaDBPlatform
) {
	$table->addColumn( 'code', 'string', ['length' => 64, 'customSchemaOptions' => ['charset' => 'binary']] );
} else {
	$table->addColumn( 'code', 'string', ['length' => 64]] );
}

$table->addColumn( 'label', 'string', ['length' => 255] );
$table->addColumn( 'pos', 'integer', ['default' => 0] );
$table->addColumn( 'status', 'smallint', [] );
$table->addColumn( 'mtime', 'datetime', [] );
$table->addColumn( 'ctime', 'datetime', [] );
$table->addColumn( 'editor', 'string', ['length' => 255] );

$table->setPrimaryKey( ['id'] );
$table->addUniqueIndex( ['domain', 'code'] );
$table->addIndex( ['status', 'pos'] );

foreach( $from->getMigrateToSql( $to, $conn->getDatabasePlatform() ) as $sql ) {
	$conn->executeStatement( $sql );
}

Doctrine Migration drawbacks

Doctrine Migration relies on migration classes that are named by the time they have been created to ensure a certain order. Furthermore, it stores which migrations has been executed in a table of your database. There are three major problems that arise from that:

  • dependencies between 3rd party extensions
  • tracking changes is out of sync
  • data loss when using down()

If your application supports 3rd party extensions, these extensions are likely to add columns to existing tables and migrate data themselves. As there’s no way to define dependencies between migrations, it can get almost impossible to run migrations in an application with several 3rd party extensions without conflicts. To avoid that, Upscheme offers easy to use before() and after() methods in each migration task where the tasks can define its dependencies to other tasks.

Because Doctrine Migrations uses a database table to record which migration already has been executed, these records can get easily out of sync in case of problems. Contrary, Upscheme only relies on the actual schema so it’s possible to upgrade from any state, regardless of what has happend before.

Doctrine Migrations also supports the reverse operations in down() methods so you can roll back migrations which Upscheme does not. Experience has shown that it’s often impossible to roll back migrations, e.g. after adding a new colum, migrating the data of an existing column and dropping the old column afterwards. If the migration of the data was lossy, you can’t recreate the same state in a down() method. The same is the case if you’ve dropped a table. Thus, Upscheme only offers scheme upgrading but no downgrading to avoid implicit data loss.

Database support

Upscheme uses Doctrine DBAL for abstracting from different database server implementations. DBAL supports all major relationsal database management systems (RDBMS) but with a different level of support for the available features:

Good support:

  • MySQL
  • MariaDB
  • PostgreSQL
  • SQLite
  • SQL server

Limited support:

  • DB2
  • Oracle
  • SQL Anywhere

Integrating Upscheme

After you’ve installed the aimeos/upscheme package using composer, you can use the Up class to execute your migration tasks:

$config = [
	'driver' => 'pdo_mysql',
	'host' => '127.0.0.1',
	'dbname' => '<database>',
	'user' => '<dbuser>',
	'password' => '<secret>'
];

\Aimeos\Upscheme\Up::use( $config, 'src/migrations' )->up();

The Up::use() method requires two parameters: The database configuration and the path(s) to the migration tasks. For the config, the array keys and the values for driver must be supported by Doctrine DBAL. Available drivers are:

  • pdo_mysql
  • pdo_pgsql
  • pdo_sqlite
  • pdo_sqlsrv
  • pdo_oci
  • ibm_db2
  • mysqli
  • oci8
  • sqlanywhere
  • sqlsrv

Some databases require different parameters, most notable SQLite and Oracle:

SQLite:

$config = [
	'driver' => 'pdo_sqlite',
	'path' => 'path/to/file.sq3'
];

Oracle:

$config = [
	'driver' => 'pdo_oci',
	'host' => '<host or IP>',
	'dbname' => '<SID or service name (Oracle 18+)>',
	'service' => true, // for Oracle 18+ only
	'user' => '<dbuser>',
	'password' => '<secret>'
];

If you didn’t use Doctrine DBAL before, your database configuration may have a different structure and/or use different values for the database type. Upscheme allows you to register a custom method that transforms your configration into valid DBAL settings, e.g.:

\Aimeos\Upscheme\Up::macro( 'connect', function( array $cfg ) {

	return \Doctrine\DBAL\DriverManager::getConnection( [
		'driver' => $cfg['adapter'],
		'host' => $cfg['host'],
		'dbname' => $cfg['database'],
		'user' => $cfg['username'],
		'password' => $cfg['password']
	] );
} );

Upscheme also supports several database connections which you can distinguish by their key name:

$config = [
	'db' => [
		'driver' => 'pdo_mysql',
		'host' => '127.0.0.1',
		'dbname' => '<database>',
		'user' => '<dbuser>',
		'password' => '<secret>'
	],
	'temp' => [
		'driver' => 'pdo_sqlite',
		'path' => '/tmp/mydb.sqlite'
	]
];

\Aimeos\Upscheme\Up::use( $config, 'src/migrations' )->up();

Of course, you can also pass several migration paths to the Up class:

\Aimeos\Upscheme\Up::use( $config, ['src/migrations', 'ext/migrations'] )->up();

To enable (debugging) output, use the verbose() method:

\Aimeos\Upscheme\Up::use( $config, 'src/migrations' )->verbose()->up(); // most important only
\Aimeos\Upscheme\Up::use( $config, 'src/migrations' )->verbose( 'vv' )->up(); // more verbose
\Aimeos\Upscheme\Up::use( $config, 'src/migrations' )->verbose( 'vvv' )->up(); // debugging

Writing migrations

A migration task only requires implementing the up() method and must be stored in one of the directories passed to the Up class:

<?php

namespace Aimeos\Upscheme\Task;
use Aimeos\Upscheme\Schema\Table;


return new class( $this ) extends Base {

	public function up()
	{
		$this->db()->table( 'test', function( Table $t ) {
			$t->id();
			$t->string( 'label' );
			$t->bool( 'status' );
		} );
	}
};

In your PHP file, always include the namespace statement first. The use statement is optional and only needed as shortcut for the type hint for the closure function argument. Your class also has to extend from the “Base” task class or implement the “Iface” task interface.

Alternatively to anonymous classes, you can use named classes for migration tasks:

<?php

namespace Aimeos\Upscheme\Task;
use Aimeos\Upscheme\Schema\Table;


class TestTable extends Base
{
	public function up()
	{
		$this->db()->table( 'test', function( Table $t ) {
			$t->id();
			$t->string( 'label' );
			$t->bool( 'status' );
		} );
	}
}

The file your class is stored in must have the same name (case sensitive) as the class itself and the .php suffix, e.g:

class TestTable -> TestTable.php

Naming migrations

There’s no strict convention how to name migration task files. You can either name them by what they do (e.g. “CreateTestTable.php”), what they operate on (e.g. “TestTable.php”) or even use a timestamp (e.g. “20201231_Test.php”).

If the tasks doesn’t contain dependencies, they are sorted and executed in alphabethical order according to the file name and the sorting would be:

20201231_Test.php
CreateTestTable.php
TestTable.php

Dependencies

To specify dependencies to other migration tasks, use the after() and before() methods. Your task is executed after the tasks returned by after() and before the tasks returned by before():

return new class( $this ) extends Base {

	public function after() : array
	{
		return ['CreateRefTable'];
	}

	public function before() : array
	{
		return ['InsertTestData'];
	}
}

The task names are the file names of the tasks without the .php suffix. If the example migration is stored in the file TestTable.php, the order of execution would be:

CreateRefTable.php -> TestTable.php -> InsertTestData.php

Messages

To output messages in your migration task use the info() method:

$this->info( 'some message' );
$this->info( 'more verbose message', 'vv' );
$this->info( 'very verbose debug message', 'vvv' );

The second parameter is the verbosity level and none or v are standard messages, vv are messages that are only displayed if more verbosity is wanted while vvv is for debugging messages. There’s also a third parameter for indenting the messages:

$this->info( 'some message' );
$this->info( 'second level message', 'v', 1 );
$this->info( 'third level message', 'v', 2 );

This will display:

some message
  second level message
    third level message

Prerequisite is that the verbose() method of the Up class has been called before:

\Aimeos\Upscheme\Up::use( $config, '...' )->verbose()->up();

Schemas

In the up() method, you have access to the database schema using the db() method. In case you’ve passed more than one database configuration to Up::use(), you can access the different schemas by their configuration key:

// $config = ['db' => [...], 'temp' => [...]];
// \Aimeos\Upscheme\Up::use( $config, '...' )->up();

$this->db();
$this->db( 'db' );
$this->db( 'temp' );

If you pass no config key or one that doesn’t exist, the first configuration is returned (“db” in this case). By using the available methods of the database schema object, you can add, update or drop tables, columns, indexes and other database objects. Also, you can use insert(), select(), update(), delete() and stmt() to manipulate the records of the tables.

After each migration task, the schema updates made in the task are automatically applied to the database. If you need to persist a change immediately because you want to insert data, call $this->db()->up() yourself. The up() method is also available in any table, sequence, and column object so you can call up() everywhere.

In cases you need two different database connections because you want to execute SELECT and INSERT/UPDATE/DELETE statements at the same time, pass TRUE as second parameter to db() to get the database schema including a new connection:

$db1 = $this->db();
$db2 = $this->db( 'db', true );

foreach( $db1->select( 'users', ['status' => false] ) as $row ) {
	$db2->insert( 'oldusers', $row );
}

$db2->delete( 'users', ['status' => false] );

All schema changes made are applied to the database before the schema with the new connection is returned. To avoid database connections to pile up until the database server rejects new connections, always calll close() for new connections created by db( '<name>', true ):

$db2->close();

Generate from database

Instead of writing migrations for your database objects by hand, you can generate the migration files automatically using:

$config = [
	'db' => [
		'driver' => 'pdo_mysql',
		'host' => '127.0.0.1',
		'dbname' => '<database>',
		'user' => '<dbuser>',
		'password' => '<secret>'
	]
];

\Aimeos\Upscheme\Up::use( $config, 'migrations' )->create();

This will generate one file for each sequence, table and view in the passed directory (./migrations/ in this example). If you have several databases and want to create migrations for all of them at once, pass the connection keys from the configuration to create():

$config = [
	'db' => [
		'driver' => 'pdo_mysql',
		// ...
	],
	'order' => [
		'driver' => 'pdo_oci',
		// ...
	]
];

\Aimeos\Upscheme\Up::use( $config, 'migrations' )->create( ['db', 'order'] );

Database

Accessing objects

You get the database schema object in your task by calling $this->db() as described in the schema section. It gives you full access to the database schema including all tables, sequences and other schema objects:

$table = $this->db()->table( 'users' );
$seq = $this->db()->sequence( 'seq_users' );

If the table or seqence doesn’t exist, it will be created. Otherwise, the existing table or sequence object is returned. In both cases, you can modify the objects afterwards and add e.g. new columns to the table.

Checking existence

You can test for tables, columns, indexes, foreign keys and sequences using the database schema returned by $this->db():

$db = $this->db();

if( $db->hasTable( 'users' ) ) {
    // The "users" table exists
}

if( $db->hasColumn( 'users', 'name' ) ) {
    // The "name" column in the "users" table exists
}

if( $db->hasIndex( 'users', 'idx_name' ) ) {
    // The "idx_name" index in the "users" table exists
}

if( $db->hasForeign( 'users_address', 'fk_users_id' ) ) {
    // The foreign key "fk_users_id" in the "users_address" table exists
}

if( $db->hasSequence( 'seq_users' ) ) {
    // The "seq_users" sequence exists
}

if( $db->hasView( 'testview' ) ) {
    // The "testview" view exists
}

Renaming objects

The database object returned by $this->db() offers the possibility to rename tables, columns and indexes using the renameTable(), renameColumn() and renameIndex():

$db = $this->db();

// Renames the table "users" to "accounts"
$db->renameTable( 'users', 'account' );

// Renames the column "label" to "name" in the "users" table
$db->renameColumn( 'users', 'label', 'name' );

// Renames the column "idx_label" to "idx_name" in the "users" table
$db->renameIndex( 'users', 'idx_label', 'idx_name' );

Removing objects

The database object returned by $this->db() also has methods for dropping tables, columns, indexes, foreign keys and sequences:

$db = $this->db();

// Drops the foreign key "fk_users_id" from the "users_address" table
$db->dropForeign( 'users_address', 'fk_users_id' );

// Drops the "idx_name" index from the "users" table
$db->dropIndex( 'users', 'idx_name' );

// Drops the "name" column from the "users" table
$db->dropColumn( 'users', 'name' );

// Drops the "seq_users" sequence
$db->dropSequence( 'seq_users' );

// Drops the "users" table
$db->dropTable( 'users' );

// Drops the "testview" view
$db->dropView( 'testview' );

If the table, column, index, foreign key or sequence doesn’t exist, it is silently ignored. For cases where you need to know if they exist, use the hasTable(), hasColumn(), hasIndex(), hasForeign() and hasSeqence() methods before like described in the “Checking for existence” section.

Query/modify table rows

The insert(), select(), update() and delete() methods are an easy way to add, retrieve, modify and remove rows in any table:

$this->db()->transaction( function( $db ) {

	$db2 = $this->db( 'db', true );

	foreach( $db2->select( 'users', ['status' => false] ) as $row )
	{
		$db->insert( 'newusers', ['userid' => $row['id'], 'status' => true] );
		$db->update( 'users', ['refid' => $db->lastId()], ['id' => $row['id']] );
	}

	$db->delete( 'newusers', ['status' => false] );
	$db2->close();
} );

If you use select() simultaniously with insert(), update() or delete(), you must create a second database connection because the select() statement will return rows while you send new commands to the database server. This only works on separate connections, not on the same.

To wrap all delete/insert/update operations into a transaction, you must use the transaction() method of the database object:

$this->db()->transaction( function( $db ) {
	// $db->insert( ... )
	// $db->update( ... )
	// $db->delete( ... )
} );

This ensures that all write operations are performed atomically or none of them in case of an error. The transaction() method ensures that the transaction is committed or rolled back automatically after your anonymous function returns control to the method.

If you need additional parameters within your anonymous function, you can hand them over in the use list of your function:

$userid = 123;
$this->db()->transaction( function( $db ) use ( $userid ) {
	$db->insert( 'newusers', ['userid' => userid, 'status' => true] );
} );

You can only pass simple key/value pairs for conditions to the methods which are combined by AND. If you need more complex queries, use the stmt() instead:

$db = $this->db();

$result = $db->stmt()->select( 'id', 'name' )
	->from( 'users' )
	->where( 'status != ?' )
	->setParameter( 0, false )
	->executeQuery();

$db->stmt()->delete( 'users' )
	->where( 'status != ?' )
	->setParameter( 0, false )
	->executeStatement();

$db->stmt()->update( 'users' )
	->set( 'status', '?' )
	->where( 'status != ?' )
	->setParameters( [true, false] )
	->executeStatement();

The stmt() method returns a Doctrine\DBAL\Query\QueryBuilder object which enables you to build more advanced statement. Please have a look into the Doctrine Query Builder documentation for more details.

If you want to use values directly in a SQL statement (use prepared statements for security reasons whenever possible!), you have to quote the values using the q() method:

$db = $this->db();

$result = $db->stmt()->select( '*' )->from( 'products' )
	->where( 'status = ' . $db->q( $_GET['status'] ) )->executeQuery();

Similarly, if your schema contains reserved keywords, e.g. as column names, you have to quote them as well using the qi() method:

$db = $this->db();

$result = $db->stmt()->select( $db->qi( 'key' ) )->from( 'products' )->executeQuery();

Executing custom SQL

Doctrine only supports a common subset of SQL statements and not all possibilities the database vendors have implemented. To remove that limit, Upscheme offers the exec(), for() and query() methods to execute custom SQL statements not supported by Doctrine DBAL.

To execute custom SQL queries use the query() method which returns a result set you can iterate over:

$sql = 'SELECT id, label, status FROM product WHERE label LIKE ?';
$result = $this->db()->query( $sql, ['test%'] );

foreach( $result->iterateAssociative() as $row ) {
	// ...
}

For all other SQL statements use the exec() method wich returns the number of affected rows:

$sql = 'UPDATE product SET status=? WHERE status=?';
$num = $this->db()->exec( $sql, [1, 0] );

Using the for() method, you can also execute statements depending on the database platform:

$this->db()->for( 'mysql', 'CREATE FULLTEXT INDEX idx_text ON product (text)' );

Specifying the database platform is very useful for creating special types of indexes where the syntax differs between the database implementations.

Database methods

DB::__call()

Calls custom methods or passes unknown method calls to the Doctrine schema object

public function __call( string $method, array $args )
  • @param string $method Name of the method
  • @param array<mixed> $args Method parameters
  • @return mixed Return value of the called method

Examples:

You can register custom methods that have access to the class properties of the Upscheme DB object:

\Aimeos\Upscheme\Schema\DB::macro( 'hasFkIndexes', function( $val ) {
	return $this->to->hasExplicitForeignKeyIndexes();
} );

$db->hasFkIndexes();
// returns true/false

Available class properties are:

$this->from : Original Doctrine database schema representing the current database

$this->to : Doctrine database schema containing the changes made up to now

$this->conn : Doctrine database connection

$this->up : Upscheme object

Furthermore, you can call any Doctrine schema method from src/Schema/Schema.php directly, e.g.:

$db->hasExplicitForeignKeyIndexes();
DB::close()

Closes the database connection

public function close() : void

Call close() only for DB schema objects created with $this->db( '...', true ). Otherwise, you will close the main connection and DBAL has to reconnect to the server which will degrade performance!

Examples:

$db = $this->db( 'temp', true );
$db->dropTable( 'test' );
$db->close();
DB::delete()

Deletes the records from the given table

public function delete( string $table, array $conditions = [] ) : self
  • @param string $table Name of the table
  • @param array<string,mixed> $conditions Key/value pairs of column names and value to compare with
  • @return self Same object for fluid method calls

Warning: The condition values are escaped but the table name and condition column names are not! Only use fixed strings for table name and condition column names but no external input!

Examples:

$db->delete( 'test', ['status' => false, 'type' => 'old'] );
$db->delete( 'test' );

Several conditions passed in the second parameter are combined by “AND”. If you need more complex statements, use the stmt() method instead.

DB::dropColumn()

Drops the column given by its name if it exists

public function dropColumn( string $table, $name ) : self
  • @param string $table Name of the table the column belongs to
  • @param array<string>|string $name Name of the column or columns
  • @return self Same object for fluid method calls

Examples:

$db->dropColumn( 'test', 'oldcol' );
$db->dropColumn( 'test', ['oldcol', 'oldcol2'] );

If the column or one of the columns doesn’t exist, it will be silently ignored.

DB::dropForeign()

Drops the foreign key constraint given by its name if it exists

public function dropForeign( string $table, $name ) : self
  • @param string $table Name of the table the foreign key constraint belongs to
  • @param array<string>|string $name Name of the foreign key constraint or constraints
  • @return self Same object for fluid method calls

Examples:

$db->dropForeign( 'test', 'fk_old' );
$db->dropForeign( 'test', ['fk_old', 'fk_old2'] );

If the foreign key constraint or one of the constraints doesn’t exist, it will be silently ignored.

DB::dropIndex()

Drops the index given by its name if it exists

public function dropIndex( string $table, $name ) : self
  • @param string $table Name of the table the index belongs to
  • @param array<string>|string $name Name of the index or indexes
  • @return self Same object for fluid method calls

Examples:

$db->dropIndex( 'test', 'idx_old' );
$db->dropIndex( 'test', ['idx_old', 'idx_old2'] );

If the index or one of the indexes doesn’t exist, it will be silently ignored.

DB::dropSequence()

Drops the sequence given by its name if it exists

public function dropSequence( $name ) : self
  • @param array<string>|string $name Name of the sequence or sequences
  • @return self Same object for fluid method calls

Examples:

$db->dropSequence( 'seq_old' );
$db->dropSequence( ['seq_old', 'seq_old2'] );

If the sequence or one of the sequences doesn’t exist, it will be silently ignored.

DB::dropTable()

Drops the table given by its name if it exists

public function dropTable( $name ) : self
  • @param array<string>|string $name Name of the table or tables
  • @return self Same object for fluid method calls

Examples:

$db->dropTable( 'test' );
$db->dropTable( ['test', 'test2'] );

If the table or one of the tables doesn’t exist, it will be silently ignored.

DB::dropView()

Drops the view given by its name if it exists

public function dropView( $name ) : self
  • @param array<string>|string $name Name of the view or views
  • @return self Same object for fluid method calls

Examples:

$db->dropView( 'test' );
$db->dropView( ['test', 'test2'] );

If the view or one of the views doesn’t exist, it will be silently ignored.

DB::exec()

Executes a custom SQL statement

public function exec( string $sql, array $params = [], array $types = [] ) : int
  • @param string $sql Custom SQL statement
  • @param array<int|string,mixed> $params List of positional parameters or associative list of placeholders and parameters
  • @param array<int|string,mixed> $types List of DBAL data types for the positional or associative placeholder parameters
  • @return int Number of affected rows

The database changes are not applied immediately so always call up() before executing custom statements to make sure that the tables you want to use has been created before!

Examples:

$sql = 'UPDATE product SET status=? WHERE status=?';
$num = $this->db()->exec( $sql, [1, 0] );
DB::for()

Executes a custom SQL statement if the database is of the given type

public function for( $type, $sql ) : self
  • @param array<string>|string $type Database type the statement should be executed for
  • @param array<string>|string $sql Custom SQL statement or statements
  • @return self Same object for fluid method calls

Available database platform types are:

  • mysql
  • mariadb
  • postgresql
  • sqlite
  • sqlserver
  • oracle
  • db2

The database changes are not applied immediately so always call up() before executing custom statements to make sure that the tables you want to use has been created before!

Examples:

$db->for( 'mysql', 'CREATE INDEX idx_test_label ON test (label(16))' );

$db->for( ['mysql', 'sqlite'], [
	'DROP INDEX unq_test_status',
	'UPDATE test SET status = 0 WHERE status IS NULL',
] );
DB::hasColumn()

Checks if the column or columns exists

public function hasColumn( string $table, $name ) : bool
  • @param string $table Name of the table the column belongs to
  • @param array<string>|string $name Name of the column or columns
  • @return bool TRUE if the columns exists, FALSE if not

Examples:

$db->hasColumn( 'test', 'testcol' );
$db->hasColumn( 'test', ['testcol', 'testcol2'] );
DB::hasForeign()

Checks if the foreign key constraints exists

public function hasForeign( string $table, $name ) : bool
  • @param string $table Name of the table the foreign key constraint belongs to
  • @param array<string>|string $name Name of the foreign key constraint or constraints
  • @return bool TRUE if the foreign key constraint exists, FALSE if not

Examples:

$db->hasForeign( 'test', 'fk_testcol' );
$db->hasForeign( 'test', ['fk_testcol', 'fk_testcol2'] );
DB::hasIndex()

Checks if the indexes exists

public function hasIndex( string $table, $name ) : bool
  • @param string $table Name of the table the index belongs to
  • @param array<string>|string $name Name of the index or indexes
  • @return bool TRUE if the index exists, FALSE if not

Examples:

$db->hasIndex( 'test', 'idx_test_col' );
$db->hasIndex( 'test', ['idx_test_col', 'idx_test_col2'] );
DB::hasSequence()

Checks if the sequences exists

public function hasSequence( $name ) : bool
  • @param array<string>|string $name Name of the sequence or sequences
  • @return bool TRUE if the sequence exists, FALSE if not

Examples:

$db->hasSequence( 'seq_test' );
$db->hasSequence( ['seq_test', 'seq_test2'] );
DB::hasTable()

Checks if the tables exists

public function hasTable( $name ) : bool
  • @param array<string>|string $name Name of the table or tables
  • @return bool TRUE if the table exists, FALSE if not

Examples:

$db->hasTable( 'test' );
$db->hasTable( ['test', 'test2'] );
DB::hasView()

Checks if the views exists

public function hasView( $name ) : bool
  • @param array<string>|string $name Name of the view or views
  • @return bool TRUE if the view exists, FALSE if not

Examples:

$db->hasView( 'test' );
$db->hasView( ['test', 'test2'] );
DB::insert()

Inserts a record into the given table

	public function insert( string $table, array $data ) : self
  • @param string $table Name of the table
  • @param array<string,mixed> $data Key/value pairs of column name/value to insert
  • @return self Same object for fluid method calls

Examples:

$db->insert( 'test', ['label' => 'myvalue', 'status' => true] );
DB::lastId()

Returns the ID of the last inserted row into any database table

public function lastId() : string
  • @return string Generated ID from the database

Caution: This doesn’t work for the Oracle platform because Doctrine DBAL doesn’t support Oracle IDENTITY columns at the moment.

Examples:

$db->lastId();
DB::name()

Returns the name of the database

public function name() : string
  • @return string Database name

Examples:

$db->name();
DB::q()

Quotes a value

public function q( $value, $type = \Doctrine\DBAL\ParameterType::STRING ) : string
  • @param mixed $value Value to use in a non-prepared SQL query
  • @param mixed $type DBAL parameter type
  • @return string Quoted value

Examples:

$result = $db->stmt()->select( '*' )->from( 'products' )
	->where( 'status = ' . $db->q( $_GET['status'] ) )->executeQuery();
DB::qi()

Quotes a database identifier

public function qi( string $identifier ) : string
  • @param string $identifier Identifier like table or column name
  • @return string Quoted identifier

Examples:

$result = $db->stmt()->select( $db->qi( 'key' ) )->from( 'products' )->executeQuery();
DB::query()

Executes a custom SQL query

public function query( string $sql, array $params = [], array $types = [] ) : \Doctrine\DBAL\Result
  • @param string $sql Custom SQL statement
  • @param array<int|string,mixed> $params List of positional parameters or associative list of placeholders and parameters
  • @param array<int|string,mixed> $types List of DBAL data types for the positional or associative placeholder parameters
  • @return \Doctrine\DBAL\Result DBAL result set object

Examples:

$result = $db->query( 'SELECT id, label, status FROM product WHERE label LIKE ?', ['test%'] );

foreach( $result->iterateAssociative() as $row ) {
	// ...
}

Tip: Check the DBAL methods for retrieving data from src/Result.php for more information.

DB::renameColumn()

Renames a column or a list of columns

public function renameColumn( string $table, $from, string $to = null ) : self
  • @param string $table Name of the table
  • @param array<string,string>|string $from Column name or array of old/new column names
  • @param string|null $to New column name ignored if first parameter is an array
  • @return self Same object for fluid method calls

If the column doesn’t exist yet, the method will succeed but nothing will happen. No call to up() is required.

Limitations

  • SQLite since 3.25.0

Examples:

// single column
$db->renameColumn( 'testtable', 'test_col', 'test_column' );

// rename several columns at once
$db->renameColumn( 'testtable', ['tcol' => 'testcol', 'tcol2' => 'testcol2'] );
DB::renameIndex()

Renames a column or a list of columns

public function renameIndex( string $table, $from, string $to = null ) : self
  • @param string $table Name of the table
  • @param array<string,string>|string $from Index name or array of old/new index names
  • @param string|null $to New index name ignored if first parameter is an array
  • @return self Same object for fluid method calls

If the index doesn’t exist yet, the method will succeed but nothing will happen. No call to up() is required.

Examples:

// single index
$db->renameIndex( 'testtable', 'idxcol', 'idx_column' );

// rename several indexes at once
$db->renameIndex( 'testtable', ['idxcol' => 'idx_column', 'idxcol2' => 'idx_column2'] );
DB::renameTable()

Renames a table or a list of tables

public function renameTable( $from, string $to = null ) : self
  • @param array<string,string>|string $from Table name or array of old/new table names
  • @param string|null $to New table name ignored if first parameter is an array
  • @return self Same object for fluid method calls
  • @throws \RuntimeException If an error occurred

If the table doesn’t exist yet, the method will succeed but nothing will happen. No call to up() is required.

Examples:

// single table
$db->renameTable( 'testtable', 'newtable' );

// rename several tables at once
$db->renameTable( ['testtable' => 'newtable', 'oldtable' => 'testtable2'] );
DB::reset()

Reloads the actual Doctrine schema for the current database

public function reset() : self
  • @return self Same object for fluid method calls

Examples:

$db->reset();
DB::select()

Returns the records from the given table

public function select( string $table, array $conditions = null ) : array
  • @param string $table Name of the table
  • @param array<string>|null $conditions Key/value pairs of column names and value to compare with
  • @return array<int,array<string,mixed>> List of associative arrays containing column name/value pairs

Examples:

$db->select( 'test', ['status' => false, 'type' => 'old'] );
$db->select( 'test' );

Several conditions passed in the second parameter are combined by “AND”. If you need more complex statements, use the stmt() method instead.

DB::sequence()

Returns the sequence object for the given name

public function sequence( string $name, \Closure $fcn = null ) : Sequence
  • @param string $name Name of the sequence
  • @param \Closure|null $fcn Anonymous function with ($sequence) parameter creating or updating the sequence definition
  • @return \Aimeos\Upscheme\Schema\Sequence Sequence object

If the sequence doesn’t exist yet, it will be created. To persist the changes in the database, you have to call up().

Examples:

$sequence = $db->sequence( 'seq_test' );

$sequence = $db->sequence( 'seq_test', function( $seq ) {
	$seq->start( 1000 )->step( 2 )->cache( 100 );
} )->up();
DB::stmt()

Returns the query builder for a new SQL statement

public function stmt() : \Doctrine\DBAL\Query\QueryBuilder
  • @return \Doctrine\DBAL\Query\QueryBuilder Query builder object

Examples:

$db->stmt()->delete( $db->qi( 'test' ) )
	->where( $db->qi( 'stat' ) . ' = ?' )->setParameter( 0, false )
	->executeStatement();

$db->stmt()->update( $db->qi( 'test' ) )
	->where( $db->qi( 'stat' ) . '', '?' )->setParameter( 0, true )
	->executeStatement();

$result = $db->stmt()->select( $db->qi( 'id' ), $db->qi( 'code' ) )
	->from( $db->qi( 'test' ) )
	->where( $db->qi( 'stat' ) . ' = 1' )
	->executeQuery();

while( $row = $result->fetchAssociative() ) {
    $id = $row['id'];
}

Caution: You have to quote all table and column names yourself using $db->qi() method!

For more details about the available Doctrine QueryBuilder methods, please have a look at the Doctrine documentation.

DB::table()

Returns the table object for the given name

public function table( string $name, \Closure $fcn = null ) : Table
  • @param string $name Name of the table
  • @param \Closure|null $fcn Anonymous function with ($table) parameter creating or updating the table definition
  • @return \Aimeos\Upscheme\Schema\Table Table object

If the table doesn’t exist yet, it will be created. To persist the changes in the database, you have to call up().

Examples:

$table = $db->table( 'test' );

$table = $db->table( 'test', function( $t ) {
	$t->id();
	$t->string( 'label' );
	$t->bool( 'status' );
} )->up();
DB::transaction()

Executes the given closure within a transaction

public function transaction( \Closure $fcn ) : self
  • @param \Closure $fcn Anonymous function with (\Aimeos\Upscheme\Schema $db) parameter
  • @return self Same object for fluid method calls
  • @throws \Exception If an error occurred

Examples:

$this->db()->transaction( function( $db ) {
	// $db->insert( ... )
	// $db->update( ... )
	// $db->delete( ... )
} );
DB::toArray()

Returns the objects as array from the database

public function toArray() : array
  • @return array Associative list of sequences, tables and views

Examples:

$this->db()->toArray();

The structure of the returned array is:

[
	'sequence' => [
		'testseq' => [
			'name' => 'testseq',
			'cache' => null,
			'start' => 1000,
			'step' => 1
		]
	],
	'table' => [
		'testtable' => [
			'name' => 'testtable',
			'opt' => [
				'engine' => 'InnoDB',
				'collation' => 'utf8mb4_unicode_ci',
				'charset' => 'utf8mb4',
				'autoincrement' => 1,
				'comment' => ''
			],
			'col' => [
				'id' => [
					'name' => 'id',
					'type' => 'integer',
					'length' => null,
					'precision' => null,
					'scale' => 0,
					'null' => false,
					'seq' => 1
					'default' => null,
					'fixed' => false,
					'unsigned' => false,
					'comment' => '',
					'opt' => []
				],
				'parentid' => [
					'name' => 'parentid',
					'type' => 'bigint',
					'length' => null,
					'precision' => null,
					'scale' => 0,
					'null' => false,
					'seq' => false,
					'default' => null,
					'fixed' => false,
					'unsigned' => false,
					'comment' => '',
					'opt' => []
				],
				'label' => [
					'name' => 'label',
					'type' => 'string',
					'length' => 255,
					'precision' => null,
					'scale' => 0,
					'null' => false,
					'seq' => false,
					'default' => null,
					'fixed' => false,
					'unsigned' => false,
					'comment' => '',
					'opt' => [
						'charset' => 'utf8mb4',
						'collation' => 'utf8mb4_unicode_ci'
					]
				]
			],
			'index' => [
				'PRIMARY' => [
					'columns' => [
							0 => 'id'
						],
					'name' => 'PRIMARY',
					'flags' => [],
					'options' => [
							'lengths' => [
									0 => null
								]
						],
					'unique' => 1,
					'primary' => 1
				],
			],
			'foreign' => [
				'FK_6C73FFCA343B91AE' => [
					'localcol' => [
						0 => 'parentid'
					],
					'fktable' => 'test',
					'fkcol' => [
						0 => 'id'
					],
					'name' => 'FK_6C73FFCA343B91AE',
					'onDelete' => 'CASCADE',
					'onUpdate' => 'CASCADE'
				]
			]
		]
	],
	'view' => [
		'testview' => [
			'name' => 'testview',
			'sql' => 'select `testtable`.`id` AS `id`,`testtable`.`label` AS `label` from `testtable`'
		]
	]
]
DB::type()

Returns the type of the database

public function type() : string
  • @return string Database type

Possible values are:

  • db2
  • mariadb
  • mysql
  • oracle
  • postgresql
  • sqlite
  • sqlserver

Examples:

$type = $db->type();
DB::up()

Applies the changes to the database schema

public function up() : self
  • @return self Same object for fluid method calls

Examples:

$db->up();
DB::update()

Updates the records from the given table

public function update( string $table, array $data, array $conditions = [] ) : self
  • @param string $table Name of the table
  • @param array<string,mixed> $data Key/value pairs of column name/value to update
  • @param array<string,mixed> $conditions Key/value pairs of column names and value to compare with
  • @return self Same object for fluid method calls

Examples:

$db->update( 'test', ['status' => true] );
$db->update( 'test', ['status' => true], ['status' => false, 'type' => 'new'] );

Several conditions passed in the second parameter are combined by “AND”. If you need more complex statements, use the stmt() method instead.

DB::view()

Creates a view with the given name if it doesn’t exist yet

public function view( string $name, string $sql, $for = null ) : self
  • @param string $name Name of the view
  • @param string $sql SELECT statement for populating the view
  • @param array<string>|string|null $for Database type this SQL should be used for (“mysql”, “mariadb”, “postgresql”, “sqlite”, “sqlserver”, “oracle”, “db2”)
  • @return self Same object for fluid method calls

If the view doesn’t exist yet, it will be created. Otherwise, nothing will happen.

Examples:

$db->view( 'testview', 'SELECT * FROM testtable' );
$db->view( 'testview', 'SELECT id, label, status FROM testtable WHERE status = 1' );
$db->view( 'testview', 'SELECT * FROM `testtable` WHERE `status` = 1', 'mysql' );

Tables

Creating tables

The table scheme object you get by calling table() in your migration task gives you full access to the table and you can add, change or remove columns, indexes and foreign keys, e.g.:

$this->db()->table( 'test', function( $table ) {
	$table->id();
	$table->string( 'label' );
	$table->col( 'status', 'tinyint' )->default( 0 );
} );

Besides the col() method which can add columns of arbitrary types, there are some shortcut methods for types available in all database server implementations:

Column type Description
bigid BIGINT column with a sequence/autoincrement and a primary key assigned
bigint BIGINT column with a range from −9223372036854775808 to 9223372036854775807
binary VARBINARY column with up to 255 bytes
blob BLOB column with up to 2GB
bool BOOLEAN/BIT/NUMBER colum, alias for “boolean”
boolean BOOLEAN/BIT/NUMBER colum for TRUE/FALSE resp. 0/1 values
char CHAR column with a fixed number of characters
date DATE column in ISO date format ("YYYY-MM-DD) without time and timezone
datetime DATETIME column in ISO date/time format (“YYYY-MM-DD HH:mm:ss” )
tablesdatetimetz DATETIMETZ column in ISO date/time format but with varying timezone format
decimal DECIMAL column for numeric data with fixed-point precision (string in PHP)
float FLOAT column for numeric data with a 8-byte floating-point precision
guid Globally unique identifier with 36 bytes
id INTEGER column with a sequence/autoincrement and a primary key assigned
int INTEGER colum, alias for “integer”
integer INTEGER colum with a range from −2147483648 to 2147483647
json JSON column for UTF-8 encoded JSON data
smallint INTEGER colum with a range from −32768 to 32767
string VARCHAR column with up to 255 characters
text TEXT/CLOB column with up to 2GB characters
time TIME column in 24 hour “HH:MM” fromat, e.g. “05:30” or “22:15”
uuid Globally unique identifier with 36 bytes, alias for “guid”

Setting table options

MySQL (or MariaDB, etc.) supports a few options to define aspects of the table. The engine option will specify the storage engine used for the table:

$this->db()->table( 'test', function( $table ) {
	$table->opt( 'engine', 'InnoDB' );
} );

As a shortcut, it’s also possible to set the option as property:

$this->db()->table( 'test', function( $table ) {
	$table->engine = 'InnoDB';
} );

To create a temporary table, use:

$this->db()->table( 'test', function( $table ) {
	$table->temporary = true;
} );

It’s also possible to set the default charset and collation for string and text columns:

$this->db()->table( 'test', function( $table ) {
	$table->charset = 'utf8mb4';
	$table->collation = 'utf8mb4_unicode_ci';
} );

Note: Collations are also supported by PostgreSQL and SQL Server but their values are different. Thus, it’s not possible to use the same value for all server types. To circumvent that problem, use the column opt() method and pass the database server type as third parameter:

$this->db()->table( 'test', function( $table ) {
	$table->opt( 'charset', 'utf8mb4', 'mysql' );
	$table->opt( 'collation', 'utf8mb4_unicode_ci', 'mysql' );
} );

Now, the default charset and collation will be only set for MySQL database servers (or MariaDB and similar forks).

In case you need to know the current values of the table options:

$this->db()->table( 'test', function( $table ) {
	// return the used table engine (only MySQL, MariaDB, etc.)
	$engine = $table->engine;

	// returns TRUE if it's a temporary table
	$isTemp = $table->temporary;

	// return the current charset
	$charset = $table->charset;

	// return the current collation
	$collation = $table->collation;
} );

Checking table existence

To check if a table already exists, use the hasTable() method:

if( $this->db()->hasTable( 'users' ) ) {
    // The "users" table exists
}

You can check for several tables at once too:

if( $this->db()->hasTable( ['users', 'addresses'] ) ) {
    // The "users" and "addresses" tables exist
}

The hasTable() method will only return TRUE if all tables exist.

Changing tables

Besides creating and accessing tables, the table() method from the schema object can be used to update a table schema too. It accepts the table name and a closure that will receive the table schema object.

Let’s create a table named test first including three columns:

$this->db()->table( 'test', function( $table ) {
	$table->id();
	$table->string( 'label' );
	$table->col( 'status', 'tinyint' )->default( 0 );
} );

Now, we want to update the table in another migration by adding a code column and changing the default value of the existing status column:

$this->db()->table( 'test', function( $table ) {
	$table->string( 'code' );
	$table->col( 'status', 'tinyint' )->default( 1 );
} );

The changes will be persisted in the database as soon as the table() method returns so there’s no need to call up() yourself afterwards. For the available column types and options, refer to the columns section.

Renaming tables

The database object returned by $this->db() can rename tables when using the renameTable() method:

// Renames the table "users" to "accounts"
$this->db()->renameTable( 'users', 'account' );

It’s also possible to rename several tables at once if you pass an associative array which old and new names as key/value pairs:

// Renames the table "users" to "accounts" and "blog" to "posts"
$this->db()->renameTable( ['users' => 'account', 'blog' => 'posts'] );

Tables are only renamed if they exist. If a table doesn’t exist any more, no error is reported:

$this->db()->renameTable( 'notexist', 'newtable' );

In that case, the method call will succeed but nothing will happen.

Dropping tables

To remove a table, you should use the dropTable() method from the database schema:

$this->db()->dropTable( 'users' );

You can also drop several tables at once by passing the list as array:

$this->db()->dropTable( ['users', 'addresses'] );

Tables are only removed if they exist. If a table doesn’t exist any more, no error is reported:

$this->db()->dropTable( 'notexist' );

In that case, the method call will succeed but nothing will happen.

Table methods

Table::__call()

Calls custom methods or passes unknown method calls to the Doctrine table object

public function __call( string $method, array $args )
  • @param string $method Name of the method
  • @param array<mixed> $args Method parameters
  • @return mixed Return value of the called method

Examples:

You can register custom methods that have access to the class properties of the Upscheme Table object:

\Aimeos\Upscheme\Schema\Table::macro( 'addConstraint', function( array $columns ) {
	return $this->to->addUniqueConstraint( $columns );
} );

$table->addConstraint( ['col1', 'col2'] );

Available class properties are:

$this->table : Doctrine table schema

$this->up : Upscheme object

Furthermore, you can call any Doctrine table method from src/Schema/Table.php directly, e.g.:

$table->addUniqueConstraint( ['col1', 'col2'] );
Table::__get()

Returns the value for the given table option

public function __get( string $name )
  • @param string $name Table option name
  • @return mixed Table option value

The list of available table options are:

  • charset (MySQL)
  • collation (MySQL)
  • engine (MySQL)
  • temporary (MySQL)

Examples:

$engine = $table->engine;

// same as
$engine = $table->opt( 'engine' );
Table::__set()

Sets the new value for the given table option

public function __set( string $name, $value )
  • @param string $name Table option name
  • @param mixed Table option value

The list of available table options are:

  • charset (MySQL)
  • collation (MySQL)
  • engine (MySQL)
  • temporary (MySQL)

Examples:

$table->engine = 'InnoDB';

// same as
$table->opt( 'engine', 'InnoDB' );
Table::bigid()

Creates a new ID column of type “bigint” or returns the existing one

public function bigid( string $name = null ) : Column
  • @param string|null $name Name of the ID column
  • @return \Aimeos\Upscheme\Schema\Column Column object

The column gets a sequence (autoincrement) and a primary key assigned automatically. If the column doesn’t exist yet, it will be created.

Examples:

$table->bigid();
$table->bigid( 'uid' );
Table::bigint()

Creates a new column of type “bigint” or returns the existing one

public function bigint( string $name ) : Column
  • @param string $name Name of the column
  • @return \Aimeos\Upscheme\Schema\Column Column object

If the column doesn’t exist yet, it will be created.

Examples:

$table->bigint( 'testcol' );
Table::binary()

Creates a new column of type “binary” or returns the existing one

public function binary( string $name, int $length = 255 ) : Column
  • @param string $name Name of the column
  • @param int $length Length of the column in bytes
  • @return \Aimeos\Upscheme\Schema\Column Column object

If the column doesn’t exist yet, it will be created.

Examples:

$table->binary( 'testcol' );
$table->binary( 'testcol', 32 );
Table::blob()

Creates a new column of type “blob” or returns the existing one

public function blob( string $name, int $length = 0x7fff ) : Column
  • @param string $name Name of the column
  • @param int $length Length of the column in bytes
  • @return \Aimeos\Upscheme\Schema\Column Column object

The maximum length of a “blob” column is 2GB. If the column doesn’t exist yet, it will be created.

Examples:

$table->blob( 'testcol' );
$table->blob( 'testcol', 0x7fffffff );
Table::bool()

Creates a new column of type “boolean” or returns the existing one

public function bool( string $name ) : Column
  • @param string $name Name of the column
  • @return \Aimeos\Upscheme\Schema\Column Column object

This method is an alias for boolean(). If the column doesn’t exist yet, it will be created.

Examples:

$table->bool( 'testcol' );
Table::boolean()

Creates a new column of type “boolean” or returns the existing one

public function boolean( string $name ) : Column
  • @param string $name Name of the column
  • @return \Aimeos\Upscheme\Schema\Column Column object

If the column doesn’t exist yet, it will be created.

Examples:

$table->boolean( 'testcol' );
Table::char()

Creates a new column of type “char” with a fixed type or returns the existing one

public function char( string $name, int $length ) : Column
  • @param string $name Name of the column
  • @param int $length Length of the column in characters
  • @return \Aimeos\Upscheme\Schema\Column Column object

If the column doesn’t exist yet, it will be created.

Examples:

$table->char( 'testcol', 3 );
Table::col()

Creates a new column or returns the existing one

public function col( string $name, string $type = null ) : Column
  • @param string $name Name of the column
  • @param string|null $type Type of the column
  • @return \Aimeos\Upscheme\Schema\Column Column object

If the column doesn’t exist yet, it will be created.

Examples:

$table->col( 'testcol' );
$table->col( 'testcol', 'tinyint' );
Table::date()

Creates a new column of type “date” or returns the existing one

public function date( string $name ) : Column
  • @param string $name Name of the column
  • @return \Aimeos\Upscheme\Schema\Column Column object

If the column doesn’t exist yet, it will be created.

Examples:

$table->date( 'testcol' );
Table::datetime()

Creates a new column of type “datetime” or returns the existing one

public function datetime( string $name ) : Column
  • @param string $name Name of the column
  • @return \Aimeos\Upscheme\Schema\Column Column object

If the column doesn’t exist yet, it will be created.

Examples:

$table->datetime( 'testcol' );
Table::datetimetz()

Creates a new column of type “datetimetz” or returns the existing one

public function datetimetz( string $name ) : Column
  • @param string $name Name of the column
  • @return \Aimeos\Upscheme\Schema\Column Column object

If the column doesn’t exist yet, it will be created.

Examples:

$table->datetimetz( 'testcol' );
Table::decimal()

Creates a new column of type “decimal” or returns the existing one

public function decimal( string $name, int $digits, int $decimals = 2 ) : Column
  • @param string $name Name of the column
  • @param int $digits Total number of decimal digits including decimals
  • @param int $decimals Number of digits after the decimal point
  • @return \Aimeos\Upscheme\Schema\Column Column object

If the column doesn’t exist yet, it will be created.

Examples:

$table->decimal( 'testcol', 10 ); // 10 digits incl. 2 decimals
$table->decimal( 'testcol', 10, 4 ); // 10 digits incl. 4 decimals
Table::dropColumn()

Drops the column given by its name if it exists

public function dropColumn( $name ) : self
  • @param array<string>|string $name Name of the column or columns
  • @return self Same object for fluid method calls

If the column or one of the columns doesn’

README 内容过长,已截取前 64 KB。完整内容请前往 GitHub 仓库查看。

评论

暂无评论